/*
 * Copyright 2008 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkCanvas.h"

#include "TextStyle.h"
#include "config/SkUserConfig.h"
#include "core/SkPathTypes.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkBlender.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkColorType.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkMaskFilter.h"
#include "include/core/SkMesh.h"
#include "include/core/SkPath.h"
#include "include/pathops/SkPathOps.h"
#include "include/core/SkPathEffect.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRasterHandleAllocator.h"
#include "include/core/SkRegion.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTextBlob.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypes.h"
#include "include/core/SkVertices.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkSafe32.h"
#include "include/private/base/SkSpan_impl.h"
#include "include/private/base/SkTPin.h"
#include "include/private/base/SkTemplates.h"
#include "include/private/base/SkTo.h"
// #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
// #include "include/private/chromium/Slug.h"
// #include "include/utils/SkNoDrawCanvas.h"
#include "include/core/SkStrokeRec.h"
#include "private/SkShadowFlags.h"
#include "src/base/SkMSAN.h"
#include "src/core/SkCanvasPriv.h"
#include "src/core/SkDevice.h"
#include "src/core/SkImageFilterTypes.h"
#include "src/core/SkImageFilter_Base.h"
#include "src/core/SkLatticeIter.h"
#include "src/core/SkMatrixPriv.h"
#include "src/core/SkMatrixUtils.h"
#include "src/core/SkPaintPriv.h"
#include "src/core/SkRecorder.h"
#include "src/core/SkSpecialImage.h"
#include "src/core/SkSurfacePriv.h"
#include "src/core/SkTextBlobPriv.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkVerticesPriv.h"
#include "src/effects/colorfilters/SkColorFilterBase.h"
#include "src/image/SkImage_Base.h"
#include "src/pathops/SkPathOpsDebug.h"
// #include "src/effects/colorfilters/SkColorFilterBase.h"
// #include "src/image/SkSurface_Base.h"
// #include "src/text/GlyphRun.h"
// #include "src/utils/SkPatchUtils.h"
#include "src/core/SkRRectPriv.h"
#include "oh/OHRenderNode.h"

#include <algorithm>
#include <atomic>
#include <functional>
#include <memory>
#include <native_drawing/drawing_canvas.h>
#include <native_drawing/drawing_rect.h>
#include <native_drawing/drawing_sampling_options.h>
#include <new>
#include <optional>
#include <tuple>
#include <utility>
#include <vector>
#include <cstddef>

#if defined(SK_GANESH)
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrRecordingContext.h"
#include "src/gpu/ganesh/Device.h"
#include "src/utils/SkTestCanvas.h"
#endif

#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/Device.h"
#endif

#define RETURN_ON_NULL(ptr)     do { if (nullptr == (ptr)) return; } while (0)
#define RETURN_ON_FALSE(pred)   do { if (!(pred)) return; } while (0)

// This is a test: static_assert with no message is a c++17 feature,
// and std::max() is constexpr only since the c++14 stdlib.
static_assert(std::max(3,4) == 4);

using Slug = sktext::gpu::Slug;




///////////////////////////////////////////////////////////////////////////////////////////////////

/*
 *  Return true if the drawing this rect would hit every pixels in the canvas.
 *
 *  Returns false if
 *  - rect does not contain the canvas' bounds
 *  - paint is not fill
 *  - paint would blur or otherwise change the coverage of the rect
 */
bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
                                           ShaderOverrideOpacity overrideOpacity) const {
    RENDER_UNIMPLEMENTED;
    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

bool SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
    RENDER_UNIMPLEMENTED;
    return false;
}

bool SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
                             ShaderOverrideOpacity overrideOpacity) {
    RENDER_UNIMPLEMENTED;
    return false;
}

///////////////////////////////////////////////////////////////////////////////

SkCanvas::Layer::Layer(sk_sp<SkBaseDevice> device,
                       sk_sp<SkImageFilter> imageFilter,
                       const SkPaint& paint)
        : fDevice(std::move(device))
        , fImageFilter(std::move(imageFilter))
        , fPaint(paint)
        , fDiscard(false) {
    SkASSERT(fDevice);
    // Any image filter should have been pulled out and stored in 'imageFilter' so that 'paint'
    // can be used as-is to draw the result of the filter to the dst device.
    SkASSERT(!fPaint.getImageFilter());
}

SkCanvas::BackImage::BackImage(sk_sp<SkSpecialImage> img, SkIPoint loc)
                               :fImage(img), fLoc(loc) {}
SkCanvas::BackImage::BackImage(const BackImage&) = default;
SkCanvas::BackImage::BackImage(BackImage&&) = default;
SkCanvas::BackImage& SkCanvas::BackImage::operator=(const BackImage&) = default;
SkCanvas::BackImage::~BackImage() = default;

SkCanvas::MCRec::MCRec(SkBaseDevice* device) : fDevice(device) {
    SkASSERT(fDevice);
}

SkCanvas::MCRec::MCRec(const MCRec* prev) : fDevice(prev->fDevice), fMatrix(prev->fMatrix) {
    SkASSERT(fDevice);
}

SkCanvas::MCRec::~MCRec() {}

void SkCanvas::MCRec::newLayer(sk_sp<SkBaseDevice> layerDevice,
                               sk_sp<SkImageFilter> filter,
                               const SkPaint& restorePaint) {
    SkASSERT(!fBackImage);
    RENDER_UNIMPLEMENTED;
}

void SkCanvas::MCRec::reset(SkBaseDevice* device) {
    SkASSERT(!fLayer);
    SkASSERT(device);
    SkASSERT(fDeferredSaveCount == 0);
    RENDER_UNIMPLEMENTED;
}

class SkCanvas::AutoUpdateQRBounds {
public:
    explicit AutoUpdateQRBounds(SkCanvas* canvas) : fCanvas(canvas) {
        // pre-condition, fQuickRejectBounds and other state should be valid before anything
        // modifies the device's clip.
        fCanvas->validateClip();
    }
    ~AutoUpdateQRBounds() {
        fCanvas->fQuickRejectBounds = fCanvas->computeDeviceClipBounds();
        // post-condition, we should remain valid after re-computing the bounds
        fCanvas->validateClip();
    }

private:
    SkCanvas* fCanvas;

    AutoUpdateQRBounds(AutoUpdateQRBounds&&) = delete;
    AutoUpdateQRBounds(const AutoUpdateQRBounds&) = delete;
    AutoUpdateQRBounds& operator=(AutoUpdateQRBounds&&) = delete;
    AutoUpdateQRBounds& operator=(const AutoUpdateQRBounds&) = delete;
};

/////////////////////////////////////////////////////////////////////////////
// Attempts to convert an image filter to its equivalent color filter, which if possible, modifies
// the paint to compose the image filter's color filter into the paint's color filter slot.
// Returns true if the paint has been modified.
// Requires the paint to have an image filter and the copy-on-write be initialized.
static bool image_to_color_filter(SkPaint* paint) {
    SkASSERT(SkToBool(paint) && paint->getImageFilter());
    // @todo

//     SkColorFilter* imgCFPtr;
//     if (!paint->getImageFilter()->asAColorFilter(&imgCFPtr)) {
//         return false;
//     }
//     sk_sp<SkColorFilter> imgCF(imgCFPtr);
//
//     SkColorFilter* paintCF = paint->getColorFilter();
//     if (paintCF) {
//         // The paint has both a colorfilter(paintCF) and an imagefilter-that-is-a-colorfilter(imgCF)
//         // and we need to combine them into a single colorfilter.
//         imgCF = imgCF->makeComposed(sk_ref_sp(paintCF));
//     }
//
//     paint->setColorFilter(std::move(imgCF));
//     paint->setImageFilter(nullptr);
    return true;
}

/**
 *  We implement ImageFilters for a given draw by creating a layer, then applying the
 *  imagefilter to the pixels of that layer (its backing surface/image), and then
 *  we call restore() to xfer that layer to the main canvas.
 *
 *  1. SaveLayer (with a paint containing the current imagefilter and xfermode)
 *  2. Generate the src pixels:
 *      Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
 *      return (fPaint). We then draw the primitive (using srcover) into a cleared
 *      buffer/surface.
 *  3. Restore the layer created in #1
 *      The imagefilter is passed the buffer/surface from the layer (now filled with the
 *      src pixels of the primitive). It returns a new "filtered" buffer, which we
 *      draw onto the previous layer using the xfermode from the original paint.
 */
class AutoLayerForImageFilter {
public:
    // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
    // paint. It's used to determine the size of the offscreen layer for filters.
    // If null, the clip will be used instead.
    //
    // Draw functions should use layer->paint() instead of the passed-in paint.
    AutoLayerForImageFilter(SkCanvas* canvas,
                            const SkPaint& paint,
                            const SkRect* rawBounds = nullptr)
            : fPaint(paint)
            , fCanvas(canvas)
            , fTempLayerForImageFilter(false) {
        SkDEBUGCODE(fSaveCount = canvas->getSaveCount();)

        if (fPaint.getImageFilter() && !image_to_color_filter(&fPaint)) {
            // The draw paint has an image filter that couldn't be simplified to an equivalent
            // color filter, so we have to inject an automatic saveLayer().
            SkPaint restorePaint;
            restorePaint.setImageFilter(fPaint.refImageFilter());
            restorePaint.setBlender(fPaint.refBlender());

            // Remove the restorePaint fields from our "working" paint
            fPaint.setImageFilter(nullptr);
            fPaint.setBlendMode(SkBlendMode::kSrcOver);

            SkRect storage;
            if (rawBounds && fPaint.canComputeFastBounds()) {
                // Make rawBounds include all paint outsets except for those due to image filters.
                // At this point, fPaint's image filter has been moved to 'restorePaint'.
                SkASSERT(!fPaint.getImageFilter());
                rawBounds = &fPaint.computeFastBounds(*rawBounds, &storage);
            }

            canvas->fSaveCount += 1;
            (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint),
                                            SkCanvas::kFullLayer_SaveLayerStrategy);
            fTempLayerForImageFilter = true;
        }
    }

    AutoLayerForImageFilter(const AutoLayerForImageFilter&) = delete;
    AutoLayerForImageFilter& operator=(const AutoLayerForImageFilter&) = delete;
    AutoLayerForImageFilter(AutoLayerForImageFilter&&) = default;
    AutoLayerForImageFilter& operator=(AutoLayerForImageFilter&&) = default;

    ~AutoLayerForImageFilter() {
        if (fTempLayerForImageFilter) {
            fCanvas->fSaveCount -= 1;
            fCanvas->internalRestore();
        }
        SkASSERT(fCanvas->getSaveCount() == fSaveCount);
    }

    const SkPaint& paint() const { return fPaint; }

private:
    SkPaint         fPaint;
    SkCanvas*       fCanvas;
    bool            fTempLayerForImageFilter;

    SkDEBUGCODE(int fSaveCount;)
};

std::optional<AutoLayerForImageFilter> SkCanvas::aboutToDraw(
    SkCanvas* canvas,
    const SkPaint& paint,
    const SkRect* rawBounds,
    CheckForOverwrite checkOverwrite,
    ShaderOverrideOpacity overrideOpacity)
{
    RENDER_UNIMPLEMENTED;
    return std::nullopt;
}

////////////////////////////////////////////////////////////////////////////

void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
    RENDER_UNIMPLEMENTED;
}

void SkCanvas::init(sk_sp<SkBaseDevice> device) {
    RENDER_UNIMPLEMENTED;
}

SkCanvas::SkCanvas() {
    RENDER_UNIMPLEMENTED;
}

SkCanvas::SkCanvas(OH_Drawing_Canvas *drawing_canvas, OHRenderNode *drawing_node) {
    fDrawingCanvas = drawing_canvas;
    fIsRecordCanvas = true;
    fSaveCount = OH_Drawing_CanvasGetSaveCount(fDrawingCanvas);
    fInitSaveCount = fSaveCount;
    fDrawingNode = drawing_node;
    fStateStack.push_back(CanvasState());
}

SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props) {
    RENDER_UNIMPLEMENTED;
}

SkCanvas::SkCanvas(const SkIRect& bounds) {
    RENDER_UNIMPLEMENTED;
}

SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device) {
    RENDER_UNIMPLEMENTED;
}

SkCanvas::~SkCanvas() {
    if (!fIsRecordCanvas) {
        OH_Drawing_CanvasDestroy(fDrawingCanvas);
    }
}

SkCanvas::SkCanvas(const SkBitmap& bitmap): SkCanvas(bitmap, {}) {
}

SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) {
    fDrawingCanvas = OH_Drawing_CanvasCreate();
    fDrawingBitmap = bitmap.getDrawingBitmap();
    OH_Drawing_CanvasBind(fDrawingCanvas, fDrawingBitmap);
    fIsRecordCanvas = false;
    fSaveCount = OH_Drawing_CanvasGetSaveCount(fDrawingCanvas);
    fInitSaveCount = fSaveCount;
    fStateStack.push_back(CanvasState());
}

///////////////////////////////////////////////////////////////////////////////

void SkCanvas::flush() {
    // @todo
}

void SkCanvas::onFlush() {
#if defined(SK_GANESH)
    RENDER_UNIMPLEMENTED;
//     auto dContext = GrAsDirectContext(this->recordingContext());
//
//     if (dContext) {
//         dContext->flushAndSubmit();
//     }
#endif
}

SkSurface* SkCanvas::getSurface() const {
    RENDER_UNIMPLEMENTED;
    return nullptr;
}

SkISize SkCanvas::getBaseLayerSize() const {
    RENDER_UNIMPLEMENTED;
    return {};
}

SkBaseDevice* SkCanvas::topDevice() const {
    RENDER_UNIMPLEMENTED;
    return nullptr;
}

bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) {
    if (fIsRecordCanvas) {
        return false;
    }
    return this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y);
}

bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
    if (fIsRecordCanvas || dstP == nullptr) {
        return false;
    };
    OH_Drawing_Image_Info oh_info = {dstInfo.width(), dstInfo.height(), dstInfo.colorTypeOHDrawing(),
                                     dstInfo.alphaTypeOHDrawing()};
    return OH_Drawing_CanvasReadPixels(fDrawingCanvas, &oh_info, dstP, rowBytes, x, y);
}

bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) {
    SkPixmap pm;
    return bm.peekPixels(&pm) && this->readPixels(pm, x, y);
}

bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
    // @todo
    SkPixmap pm;
    if (bitmap.peekPixels(&pm)) {
        return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
    }
    return false;
}

bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes,
                           int x, int y) {
    // @todo
//     SkBaseDevice* device = this->baseDevice();
//
//     // This check gives us an early out and prevents generation ID churn on the surface.
//     // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec.
//     SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height());
//     if (!srcRect.intersect({0, 0, device->width(), device->height()})) {
//         return false;
//     }
//
//     // Tell our owning surface to bump its generation ID.
//     const bool completeOverwrite = srcRect.size() == device->imageInfo().dimensions();
//     if (!this->predrawNotify(completeOverwrite)) {
//         return false;
//     }
//
//     // This can still fail, most notably in the case of a invalid color type or alpha type
//     // conversion.  We could pull those checks into this function and avoid the unnecessary
//     // generation ID bump.  But then we would be performing those checks twice, since they
//     // are also necessary at the bitmap/pixmap entry points.
//     return device->writePixels({srcInfo, pixels, rowBytes}, x, y);
    return false;
}

//////////////////////////////////////////////////////////////////////////////

void SkCanvas::checkForDeferredSave() {
    // no need
//     if (fMCRec->fDeferredSaveCount > 0) {
//         this->doSave();
//     }
}

int SkCanvas::getSaveCount() const {
#ifdef SK_DEBUG
    int count = 0;
//     SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
//     for (;;) {
//         const MCRec* rec = (const MCRec*)iter.next();
//         if (!rec) {
//             break;
//         }
//         count += 1 + rec->fDeferredSaveCount;
//     }
//     SkASSERT(count == fSaveCount);
#endif
    if (fSubCanvas) {
        return fSaveCount + fSubCanvas->getSaveCount() - 1;
    } else {
        return fSaveCount;
    }
}

int SkCanvas::save() {
    if (fSubCanvas) {
        fSubCanvas->save();
        return this->getSaveCount() - 1;
    }

    fSaveCount += 1;
    CanvasState state;
    state.fIsLayer = fStateStack.back().fIsLayer;
    state.fSaveLayerFlag = false;
    state.fNotInv = fNotInv;
    state.fClip = fCullRect;
    state.fTransform = fCTM; 
    state.fInvTransform = fInvCTM;
    state.fClipShape = fClipShape;
    state.fPendingDifferenceShape = fPendingDifferenceShape;
    fStateStack.push_back(state);
    
    OH_Drawing_CanvasSave(fDrawingCanvas);
    return this->getSaveCount() - 1;  // return our prev value
}

void SkCanvas::doSave() {
    RENDER_UNIMPLEMENTED;
//     this->willSave();
//
//     SkASSERT(fMCRec->fDeferredSaveCount > 0);
//     fMCRec->fDeferredSaveCount -= 1;
//     this->internalSave();
}

void SkCanvas::restore() {
    if (fSubCanvas && fSubCanvas->getSaveCount() == 1) {
        finishSubCanvas();
        fNeedCreateSubCanvas = true;
    }
    if (fSubCanvas) {
        SkASSERT(fSubCanvas->getSaveCount() > 1);
        fSubCanvas->restore();
        return;
    }
    if (fSaveCount > fInitSaveCount) {
        fSaveCount -= 1;
        this->internalRestore();
    }
//     if (fMCRec->fDeferredSaveCount > 0) {
//         SkASSERT(fSaveCount > 1);
//         fSaveCount -= 1;
//         fMCRec->fDeferredSaveCount -= 1;
//     } else {
//         // check for underflow
//         if (fMCStack.count() > 1) {
//             this->willRestore();
//             SkASSERT(fSaveCount > 1);
//             fSaveCount -= 1;
//             this->internalRestore();
//             this->didRestore();
//         }
//     }
}

void SkCanvas::restoreToCount(int count) {
    // safety check
    if (count < 1) {
        count = 1;
    }

    int n = this->getSaveCount() - count;

    if (fSubCanvas && fSubCanvas->getSaveCount() - 1 < n) {
        int subCount = fSubCanvas->getSaveCount() - 1;
        int leftRestoreCount = n - subCount;
        for (int i = 0; i < subCount; ++i) {
            this->restore();
        }
        // force stop recording and ready to record a new sub picture
        finishSubCanvas();
        fNeedCreateSubCanvas = true;
        for (int i = 0; i < leftRestoreCount; ++i) {
            this->restore();
        }
    } else {
        for (int i = 0; i < n; ++i) {
            this->restore();
        }
    }
}

void SkCanvas::internalSave() {
    RENDER_UNIMPLEMENTED;
//     fMCRec = new (fMCStack.push_back()) MCRec(fMCRec);
//
//     this->topDevice()->save();
}

int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
    // @ok
    return this->saveLayer(SaveLayerRec(bounds, paint, 0));
}

int SkCanvas::saveLayer(const SaveLayerRec& rec) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (rec.fPaint && rec.fPaint->nothingToDraw()) {
        // no need for the layer (or any of the draws until the matching restore()
        this->save();
        this->clipRect({0,0,0,0});
    } else {
        SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
        this->internalSaveLayer(rec, strategy);
    }
    return this->getSaveCount() - 1;
}

int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) {
    RENDER_UNIMPLEMENTED;
    return 0;
//     if (bounds && !this->getLocalClipBounds().intersects(*bounds)) {
//         // Assuming clips never expand, if the request bounds is outside of the current clip
//         // there is no need to copy/restore the area, so just devolve back to a regular save.
//         this->save();
//     } else {
//         bool doTheWork = this->onDoSaveBehind(bounds);
//         fSaveCount += 1;
//         this->internalSave();
//         if (doTheWork) {
//             this->internalSaveBehind(bounds);
//         }
//     }
//     return this->getSaveCount() - 1;
}

// In our current design/features, we should never have a layer (src) in a different colorspace
// than its parent (dst), so we assert that here. This is called out from other asserts, in case
// we add some feature in the future to allow a given layer/imagefilter to operate in a specific
// colorspace.
static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) {
    SkASSERT(src == dst);
}

// Helper function to compute the center reference point used for scale decomposition under
// non-linear transformations.
// static skif::ParameterSpace<SkPoint> compute_decomposition_center(
//         const SkMatrix& dstToLocal,
//         const skif::ParameterSpace<SkRect>* contentBounds,
//         const skif::DeviceSpace<SkIRect>& targetOutput) {
//     // Will use the inverse and center of the device bounds if the content bounds aren't provided.
//     SkRect rect = contentBounds ? SkRect(*contentBounds) : SkRect::Make(SkIRect(targetOutput));
//     SkPoint center = {rect.centerX(), rect.centerY()};
//     if (!contentBounds) {
//         // Theoretically, the inverse transform could put center's homogeneous coord behind W = 0,
//         // but that case is handled automatically in Mapping::decomposeCTM later.
//         dstToLocal.mapPoints(&center, 1);
//     }
//
//     return skif::ParameterSpace<SkPoint>(center);
// }

// Compute suitable transformations and layer bounds for a new layer that will be used as the source
// input into 'filter'  before being drawn into 'dst' via the returned skif::Mapping.
// Null filters are permitted and act as the identity. The returned mapping will be compatible with
// the image filter.
//
// Returns an empty rect if the layer wouldn't draw anything after filtering.
// static std::pair<skif::Mapping, skif::LayerSpace<SkIRect>> get_layer_mapping_and_bounds(
//         const SkImageFilter* filter,
//         const SkMatrix& localToDst,
//         const skif::DeviceSpace<SkIRect>& targetOutput,
//         const skif::ParameterSpace<SkRect>* contentBounds = nullptr,
//         bool mustCoverDst = true,
//         SkScalar scaleFactor = 1.0f) {
//     auto failedMapping = []() {
//         return std::make_pair<skif::Mapping, skif::LayerSpace<SkIRect>>(
//                 {}, skif::LayerSpace<SkIRect>::Empty());
//     };
//
//     SkMatrix dstToLocal;
//     if (!localToDst.isFinite() ||
//         !localToDst.invert(&dstToLocal)) {
//         return failedMapping();
//     }
//
//     skif::ParameterSpace<SkPoint> center =
//             compute_decomposition_center(dstToLocal, contentBounds, targetOutput);
//     // *after* possibly getting a representative point from the provided content bounds, it might
//     // be necessary to discard the bounds for subsequent layer calculations.
//     if (mustCoverDst) {
//         contentBounds = nullptr;
//     }
//
//     // Determine initial mapping and a reasonable maximum dimension to prevent layer-to-device
//     // transforms with perspective and skew from triggering excessive buffer allocations.
//     skif::Mapping mapping;
//     if (!mapping.decomposeCTM(localToDst, filter, center)) {
//         return failedMapping();
//     }
//     // Push scale factor into layer matrix and device matrix (net no change, but the layer will have
//     // its resolution adjusted in comparison to the final device).
//     if (scaleFactor != 1.0f &&
//         !mapping.adjustLayerSpace(SkMatrix::Scale(scaleFactor, scaleFactor))) {
//         return failedMapping();
//     }
//
//     // Perspective and skew could exceed this since mapping.deviceToLayer(targetOutput) is
//     // theoretically unbounded under those conditions. Under a 45 degree rotation, a layer needs to
//     // be 2X larger per side of the prior device in order to fully cover it. We use the max of that
//     // and 2048 for a reasonable upper limit (this allows small layers under extreme transforms to
//     // use more relative resolution than a larger layer).
//     static const int kMinDimThreshold = 2048;
//     int maxLayerDim = std::max(Sk64_pin_to_s32(2 * std::max(SkIRect(targetOutput).width64(),
//                                                             SkIRect(targetOutput).height64())),
//                                kMinDimThreshold);
//
//     skif::LayerSpace<SkIRect> layerBounds;
//     if (filter) {
//         layerBounds = as_IFB(filter)->getInputBounds(mapping, targetOutput, contentBounds);
//         // When a filter is involved, the layer size may be larger than the default maxLayerDim due
//         // to required inputs for filters (e.g. a displacement map with a large radius).
//         if (layerBounds.width() > maxLayerDim || layerBounds.height() > maxLayerDim) {
//             skif::Mapping idealMapping{mapping.layerMatrix()};
//             auto idealLayerBounds = as_IFB(filter)->getInputBounds(idealMapping, targetOutput,
//                                                                    contentBounds);
//             maxLayerDim = std::max(std::max(idealLayerBounds.width(), idealLayerBounds.height()),
//                                    maxLayerDim);
//         }
//     } else {
//         layerBounds = mapping.deviceToLayer(targetOutput);
//         if (contentBounds) {
//             // For better or for worse, user bounds currently act as a hard clip on the layer's
//             // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
//             skif::LayerSpace<SkIRect> knownBounds = mapping.paramToLayer(*contentBounds).roundOut();
//             if (!layerBounds.intersect(knownBounds)) {
//                 return failedMapping();
//             }
//         }
//     }
//
//     if (layerBounds.width() > maxLayerDim || layerBounds.height() > maxLayerDim) {
//         skif::LayerSpace<SkIRect> newLayerBounds(
//                 SkIRect::MakeWH(std::min(layerBounds.width(), maxLayerDim),
//                                 std::min(layerBounds.height(), maxLayerDim)));
//         SkMatrix adjust = SkMatrix::MakeRectToRect(SkRect::Make(SkIRect(layerBounds)),
//                                                    SkRect::Make(SkIRect(newLayerBounds)),
//                                                    SkMatrix::kFill_ScaleToFit);
//         if (!mapping.adjustLayerSpace(adjust)) {
//             return failedMapping();
//         } else {
//             layerBounds = newLayerBounds;
//         }
//     }
//
//     return {mapping, layerBounds};
// }

// Ideally image filters operate in the dst color type, but if there is insufficient alpha bits
// we move some bits from color channels into the alpha channel since that can greatly improve
// the quality of blurs and other filters.
static SkColorType image_filter_color_type(SkImageInfo dstInfo) {
    if (dstInfo.bytesPerPixel() <= 4 &&
        dstInfo.colorType() != kRGBA_8888_SkColorType &&
        dstInfo.colorType() != kBGRA_8888_SkColorType) {
        // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888
        return kN32_SkColorType;
    } else {
        return dstInfo.colorType();
    }
}

// static bool can_layer_be_drawn_as_sprite(const SkMatrix& matrix, const SkISize& size) {
//     // Assume anti-aliasing and highest valid filter mode (linear) for drawing layers and image
//     // filters. If the layer can be drawn as a sprite, these can be downgraded.
//     SkPaint paint;
//     paint.setAntiAlias(true);
//     SkSamplingOptions sampling{SkFilterMode::kLinear};
//     return SkTreatAsSprite(matrix, size, sampling, paint.isAntiAlias());
// }

void SkCanvas::internalDrawDeviceWithFilter(SkBaseDevice* src,
                                            SkBaseDevice* dst,
                                            const SkImageFilter* filter,
                                            const SkPaint& paint,
                                            DeviceCompatibleWithFilter compat,
                                            SkScalar scaleFactor) {
    RENDER_UNIMPLEMENTED;
//     check_drawdevice_colorspaces(dst->imageInfo().colorSpace(),
//                                  src->imageInfo().colorSpace());
//     sk_sp<SkColorSpace> filterColorSpace = dst->imageInfo().refColorSpace(); // == src.refColorSpace
//
//     // 'filterColorType' ends up being the actual color type of the layer, so image filtering is
//     // effectively done in the layer's format. We get there in a roundabout way due to handling both
//     // regular and backdrop filters:
//     //  - For regular filters, 'src' is the layer and 'dst' is the parent device. But the layer
//     //    was constructed with a color type equal to image_filter_color_type(dst), so this matches
//     //    the layer.
//     //  - For backdrop filters, 'src' is the parent device and 'dst' is the layer, which was already
//     //    constructed as image_filter_color_type(src). Calling image_filter_color_type twice does
//     //    not change the color type, so it remains the color type of the layer.
//     const SkColorType filterColorType = image_filter_color_type(dst->imageInfo());
//
//     // 'filter' sees the src device's buffer as the implicit input image, and processes the image
//     // in this device space (referred to as the "layer" space). However, the filter
//     // parameters need to respect the current matrix, which is not necessarily the local matrix that
//     // was set on 'src' (e.g. because we've popped src off the stack already).
//     // TODO (michaelludwig): Stay in SkM44 once skif::Mapping supports SkM44 instead of SkMatrix.
//     SkMatrix localToSrc = (src->globalToDevice() * fMCRec->fMatrix).asM33();
//     SkISize srcDims = src->imageInfo().dimensions();
//
//     // Whether or not we need to make a transformed tmp image from 'src', and what that transform is
//     bool needsIntermediateImage = false;
//     SkMatrix srcToIntermediate;
//
//     skif::Mapping mapping;
//     skif::LayerSpace<SkIRect> requiredInput;
//     if (compat == DeviceCompatibleWithFilter::kYes) {
//         // Just use the relative transform from src to dst and the src's whole image, since
//         // internalSaveLayer should have already determined what was necessary. We explicitly
//         // construct the inverse (dst->src) to avoid the case where src's and dst's coord transforms
//         // were individually invertible by SkM44::invert() but their product is considered not
//         // invertible by SkMatrix::invert(). When this happens the matrices are already poorly
//         // conditioned so getRelativeTransform() gives us something reasonable.
//         SkASSERT(scaleFactor == 1.0f);
//         mapping = skif::Mapping(src->getRelativeTransform(*dst),
//                                 dst->getRelativeTransform(*src),
//                                 localToSrc);
//         requiredInput = skif::LayerSpace<SkIRect>(SkIRect::MakeSize(srcDims));
//         SkASSERT(!requiredInput.isEmpty());
//     } else {
//         // Compute the image filter mapping by decomposing the local->device matrix of dst and
//         // re-determining the required input.
//         std::tie(mapping, requiredInput) = get_layer_mapping_and_bounds(
//                 filter, dst->localToDevice(), skif::DeviceSpace<SkIRect>(dst->devClipBounds()),
//                 nullptr, true, SkTPin(scaleFactor, 0.f, 1.f));
//         if (requiredInput.isEmpty()) {
//             return;
//         }
//
//         // The above mapping transforms from local to dst's device space, where the layer space
//         // represents the intermediate buffer. Now we need to determine the transform from src to
//         // intermediate to prepare the input to the filter.
//         if (!localToSrc.invert(&srcToIntermediate)) {
//             return;
//         }
//         srcToIntermediate.postConcat(mapping.layerMatrix());
//         if (can_layer_be_drawn_as_sprite(srcToIntermediate, srcDims)) {
//             // src differs from intermediate by just an integer translation, so it can be applied
//             // automatically when taking a subset of src if we update the mapping.
//             skif::LayerSpace<SkIPoint> srcOrigin({(int) srcToIntermediate.getTranslateX(),
//                                                   (int) srcToIntermediate.getTranslateY()});
//             mapping.applyOrigin(srcOrigin);
//             requiredInput.offset(-srcOrigin);
//         } else {
//             // The contents of 'src' will be drawn to an intermediate buffer using srcToIntermediate
//             // and that buffer will be the input to the image filter.
//             needsIntermediateImage = true;
//         }
//     }
//
//     sk_sp<SkSpecialImage> filterInput;
//     if (!needsIntermediateImage) {
//         // The src device can be snapped directly
//         skif::LayerSpace<SkIRect> srcSubset(SkIRect::MakeSize(srcDims));
//         if (srcSubset.intersect(requiredInput)) {
//             filterInput = src->snapSpecial(SkIRect(srcSubset));
//
//             // TODO: For now image filter input images need to have a (0,0) origin. The required
//             // input's top left has been baked into srcSubset so we use that as the image origin.
//             mapping.applyOrigin(srcSubset.topLeft());
//         }
//     } else {
//         // We need to produce a temporary image that is equivalent to 'src' but transformed to
//         // a coordinate space compatible with the image filter
//         SkASSERT(compat == DeviceCompatibleWithFilter::kUnknown);
//         SkRect srcRect;
//         if (!SkMatrixPriv::InverseMapRect(srcToIntermediate, &srcRect,
//                                           SkRect::Make(SkIRect(requiredInput)))) {
//             return;
//         }
//
//         if (!srcRect.intersect(SkRect::Make(srcDims))) {
//             return;
//         }
//         SkIRect srcSubset = skif::RoundOut(srcRect);
//
//         if (srcToIntermediate.isScaleTranslate()) {
//             // The transform is from srcRect to requiredInput, but srcRect may have been reduced
//             // to the src dimensions, so map srcSubset back to the intermediate space to get the
//             // appropriate scaled dimensions for snapScaledSpecial.
//             skif::LayerSpace<SkIRect> requiredSubset(
//                     skif::RoundOut(srcToIntermediate.mapRect(srcRect)));
//             filterInput = src->snapSpecialScaled(srcSubset,
//                                                  {requiredSubset.width(), requiredSubset.height()});
//             if (filterInput) {
//                 // TODO: Like the non-intermediate case, we need to apply the image origin
//                 mapping.applyOrigin(requiredSubset.topLeft());
//             } // else fall through and apply transform using a draw
//         }
//
//         if (!filterInput) {
//             // Either a complex transform or the scaled copy failed so do a copy-as-draw fallback.
//             sk_sp<SkSpecialImage> srcImage = src->snapSpecial(srcSubset);
//             if (!srcImage) {
//                 return;
//             }
//             // Make a new surface and draw 'srcImage' into it with the srcToIntermediate transform
//             // to produce the final input image for the filter
//             SkBaseDevice::CreateInfo info(SkImageInfo::Make(requiredInput.width(),
//                                                             requiredInput.height(),
//                                                             filterColorType,
//                                                             kPremul_SkAlphaType,
//                                                             filterColorSpace),
//                                           SkPixelGeometry::kUnknown_SkPixelGeometry,
//                                           SkBaseDevice::TileUsage::kNever_TileUsage,
//                                           fAllocator.get());
//             sk_sp<SkBaseDevice> intermediateDevice(src->onCreateDevice(info, &paint));
//             if (!intermediateDevice) {
//                 return;
//             }
//             intermediateDevice->setOrigin(SkM44(srcToIntermediate),
//                                           requiredInput.left(), requiredInput.top());
//
//             // We use drawPaint to fill the entire device with the src input + clamp tiling, which
//             // extends the backdrop's edge pixels to the parts of 'requiredInput' that map offscreen
//             // Without this, the intermediateDevice would contain transparent pixels that may then
//             // infect blurs and other filters with large kernels.
//             SkPaint imageFill;
//             imageFill.setShader(srcImage->asShader(SkTileMode::kClamp,
//                                                    SkFilterMode::kLinear,
//                                                    SkMatrix::Translate(srcSubset.topLeft())));
//             intermediateDevice->drawPaint(imageFill);
//             filterInput = intermediateDevice->snapSpecial();
//
//             // TODO: Like the non-intermediate case, we need to apply the image origin.
//             mapping.applyOrigin(requiredInput.topLeft());
//         }
//     }
//
//     if (filterInput) {
//         const bool useNN = can_layer_be_drawn_as_sprite(mapping.layerToDevice(),
//                                                         filterInput->subset().size());
//         SkSamplingOptions sampling{useNN ? SkFilterMode::kNearest : SkFilterMode::kLinear};
//         if (filter) {
//             dst->drawFilteredImage(mapping, filterInput.get(), filterColorType, filter,
//                                    sampling, paint);
//         } else {
//             dst->drawSpecial(filterInput.get(), mapping.layerToDevice(), sampling, paint);
//         }
//     }
}

// This is similar to image_to_color_filter used by AutoLayerForImageFilter, but with key changes:
//  - image_to_color_filter requires the entire image filter DAG to be represented as a color filter
//    that does not affect transparent black (SkImageFilter::asAColorFilter)
//  - when that is met, the image filter's CF is composed around any CF that was on the draw's paint
//    since for a draw, the color filtering happens before any image filtering
//  - optimize_layer_filter only applies to the last node and does not care about transparent black
//    since a layer is being made regardless (SkImageFilter::isColorFilterNode)
//  - any extracted CF is composed inside the restore paint's CF because image filters are evaluated
//    before the color filter of a restore paint for layers.
//
// Assumes that 'filter', and thus its inputs, will remain owned by the caller. Modifies 'paint'
// to have the updated color filter and returns the image filter to evaluate on restore.
// static const SkImageFilter* optimize_layer_filter(const SkImageFilter* filter, SkPaint* paint) {
//     SkASSERT(paint);
//     SkColorFilter* cf;
//     if (filter && filter->isColorFilterNode(&cf)) {
//         sk_sp<SkColorFilter> inner(cf);
//         if (paint->getAlphaf() < 1.f) {
//             // The paint's alpha is applied after the image filter but before the paint's color
//             // filter. If there is transparency, we have to apply it between the two filters.
//             // FIXME: The Blend CF should allow composing directly at construction.
//             inner = SkColorFilters::Compose(
//                     SkColorFilters::Blend(/*src*/paint->getColor4f(), nullptr, SkBlendMode::kDstIn),
//                                           /*dst*/std::move(inner));
//             paint->setAlphaf(1.f);
//         }
//
//         paint->setColorFilter(SkColorFilters::Compose(paint->refColorFilter(), std::move(inner)));
//         SkASSERT(filter->countInputs() == 1);
//         return filter->getInput(0);
//     } else {
//         return filter;
//     }
// }

// If there is a backdrop filter, or if the restore paint has a color filter or blend mode that
// affects transparent black, then the new layer must be sized such that it covers the entire device
// clip bounds of the prior device (otherwise edges of the temporary layer would be visible).
// static bool must_cover_prior_device(const SkImageFilter* backdrop,
//                                     const SkPaint& restorePaint) {
//     const SkColorFilter* cf = restorePaint.getColorFilter();
//     if (backdrop || (cf && as_CFB(cf)->affectsTransparentBlack())) {
//         // Backdrop image filters always affect the entire (clip-limited) layer. A color filter
//         // affecting transparent black will colorize pixels that are outside the drawn bounds hint.
//         return true;
//     }
//     // A custom blender is assumed to modify transparent black; some fixed blend modes also modify
//     // transparent black and the whole layer must be used for the same reason as color filters.
//     if (auto blendMode = restorePaint.asBlendMode()) {
//         SkBlendModeCoeff src, dst;
//         if (SkBlendMode_AsCoeff(*blendMode, &src, &dst)) {
//             // If the source is (0,0,0,0), then dst is preserved as long as its coefficient
//             // evaluates to 1.0. This is true for kOne, kISA, and kISC. Anything else means the
//             // blend mode affects transparent black.
//             return dst != SkBlendModeCoeff::kOne &&
//                    dst != SkBlendModeCoeff::kISA &&
//                    dst != SkBlendModeCoeff::kISC;
//         } else {
//             // else an advanced blend mode, which preserve transparent black
//             return false;
//         }
//     } else {
//         // Blenders that aren't blend modes are assumed to modify transparent black.
//         return true;
//     }
// }

static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
    if (paint) {
        // FIXME: this is very conservative
        if ((paint->getImageFilter() &&
             as_IFB(paint->getImageFilter())->affectsTransparentBlack()) ||
            (paint->getColorFilter() &&
             as_CFB(paint->getColorFilter())->affectsTransparentBlack())) {
            return true;
        }
        const auto bm = paint->asBlendMode();
        if (!bm) {
            return true;    // can we query other blenders for this?
        }

        // Unusual blendmodes require us to process a saved layer
        // even with operations outisde the clip.
        // For example, DstIn is used by masking layers.
        // https://code.google.com/p/skia/issues/detail?id=1291
        // https://crbug.com/401593
        switch (bm.value()) {
            // For each of the following transfer modes, if the source
            // alpha is zero (our transparent black), the resulting
            // blended alpha is not necessarily equal to the original
            // destination alpha.
            case SkBlendMode::kClear:
            case SkBlendMode::kSrc:
            case SkBlendMode::kSrcIn:
            case SkBlendMode::kDstIn:
            case SkBlendMode::kSrcOut:
            case SkBlendMode::kDstATop:
            case SkBlendMode::kModulate:
                return true;
            default:
                break;
        }
    }
    return false;
}

void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
    TRACE_EVENT0("skia", TRACE_FUNC);

    if (fSubCanvas) {
        fSubCanvas->internalSaveLayer(rec, strategy);
        return;
    }

    fSaveCount += 1;
    CanvasState state;
    state.fIsLayer = true;
    state.fSaveLayerFlag = true;
    state.fNotInv = fNotInv;
    state.fClip = fCullRect;
    state.fTransform = fCTM; 
    state.fInvTransform = fInvCTM;
    state.fClipShape = fClipShape;
    state.fPendingDifferenceShape = fPendingDifferenceShape;
    if (rec.fPaint) {
        state.fPaint = *rec.fPaint;
        state.fPaint->setMaskFilter(nullptr);
        state.fPaint->setAntiAlias(true);
    }
    fStateStack.push_back(state);
    
    bool needHashClip = false;
    bool needHashBounds = false;
    // needn't update fClipShape because we will not add RenderNode when in saveLayer.
    if (rec.fBounds) {
        if (fCullRect.has_value()) {
            auto realBounds = fCTM.mapRect(*rec.fBounds);
            if (realBounds.contains(fCullRect.value())) {
                needHashClip = true;
            }
            if (!fCullRect->intersect(realBounds)) {
                fCullRect = SkRect::MakeEmpty();
            } else {
                if (!needHashClip) {
                    // not contain
                    needHashClip = true;
                    needHashBounds = true;
                }
            }
        } else {
            fCullRect = fCTM.mapRect(*rec.fBounds);
            needHashBounds = true;
        }
    } else {
        needHashClip = true;
    }
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        fHasher.push(fCTM.hash());
        
        if (needHashClip) {
            fHasher.push(GetClipShapeHash(fClipShape));
            fHasher.push(GetClipShapeHash(fPendingDifferenceShape));
        }
        if (needHashBounds) {
            fHasher.pushArray(rec.fBounds, 1);
        }
        
        if (state.fPaint) {
            fHasher.push(state.fPaint->hash());
        }
    }

    if (PaintMayAffectTransparentBlack(rec.fPaint)) {
        if (fCullRect.has_value() && fDrawBounds.has_value()) {
            fDrawBounds->join(fCullRect.value());
        } else {
            // @todo: too layer area
            fDrawBounds = std::nullopt;
        }

    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && !fCullRect->isEmpty()) {
        // default has blend.
        float costPerPixel = 1 + 1;
        if (state.fPaint) {
            costPerPixel = state.fPaint->computePerPixelCost(fCullRect.value());
        }
        float thisCost = fCullRect->width() * fCullRect->height() * (costPerPixel + 1);
        fDrawCost += thisCost;
        if (thisCost > 10) {
            fDrawCost += doRealClipCost();
        }
    }
    
    if (state.fPaint) {
        state.fPaint->updateOHPainter();
    }
    OH_Drawing_CanvasSaveLayer(fDrawingCanvas, (const OH_Drawing_Rect *)rec.fBounds,
                               (state.fPaint ? (OH_Drawing_Brush *)(*state.fPaint) : nullptr));
}

int SkCanvas::saveLayerAlphaf(const SkRect* bounds, float alpha) {
    if (alpha >= 1.0f) {
        return this->saveLayer(bounds, nullptr);
    } else {
        SkPaint tmpPaint;
        tmpPaint.setAlphaf(alpha);
        return this->saveLayer(bounds, &tmpPaint);
    }
}

void SkCanvas::internalSaveBehind(const SkRect* localBounds) {
    RENDER_UNIMPLEMENTED;
//     SkBaseDevice* device = this->topDevice();
//
//     // Map the local bounds into the top device's coordinate space (this is not
//     // necessarily the full global CTM transform).
//     SkIRect devBounds;
//     if (localBounds) {
//         SkRect tmp;
//         device->localToDevice().mapRect(&tmp, *localBounds);
//         if (!devBounds.intersect(tmp.round(), device->devClipBounds())) {
//             devBounds.setEmpty();
//         }
//     } else {
//         devBounds = device->devClipBounds();
//     }
//     if (devBounds.isEmpty()) {
//         return;
//     }
//
//     // This is getting the special image from the current device, which is then drawn into (both by
//     // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its
//     // own device, we need to explicitly copy the back image contents so that its original content
//     // is available when we splat it back later during restore.
//     auto backImage = device->snapSpecial(devBounds, /* forceCopy= */ true);
//     if (!backImage) {
//         return;
//     }
//
//     // we really need the save, so we can wack the fMCRec
//     this->checkForDeferredSave();
//
//     fMCRec->fBackImage =
//             std::make_unique<BackImage>(BackImage{std::move(backImage), devBounds.topLeft()});
//
//     SkPaint paint;
//     paint.setBlendMode(SkBlendMode::kClear);
//     this->drawClippedToSaveBehind(paint);
}

void SkCanvas::internalRestore() {
    auto& topLayer = fStateStack.back();
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && fNowDrawClipShape != topLayer.fClipShape) {
        fNeedDoClip = true;
    }
    fNotInv = topLayer.fNotInv;
    fCTM = topLayer.fTransform;
    fInvCTM = topLayer.fInvTransform;
    fCullRect = topLayer.fClip;
    fClipShape = topLayer.fClipShape;
    fPendingDifferenceShape = topLayer.fPendingDifferenceShape;
    if (topLayer.fSaveLayerFlag && fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
    }
    fStateStack.pop_back();
    OH_Drawing_CanvasRestore(fDrawingCanvas);
}

float SkCanvas::doRealClipCost() {
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && fNeedDoClip && fNowDrawClipShape != fClipShape) {
        float cost = 0.f;
        // @todo 确定clip需要付出的代价，当前clip认为它会进行一次全屏stenci的写入
        if (std::holds_alternative<SkRRect>(fClipShape) || std::holds_alternative<SkPath>(fClipShape)) {
            SkRect area = maxDrawArea();
            cost += area.width() * area.height();
            fNowDrawClipShape = fClipShape;
        }
        fNeedDoClip = false;
        return cost;
    }
    return 0.f;
}

SkRect SkCanvas::maxDrawArea() {
    SkRect area;
    float maxDeviceArea = fMaxDeviceFrame.height() * fMaxDeviceFrame.width();
    if (fCullRect.has_value()) {
        area = fCullRect.value();
    } else {
        area = fMaxDeviceFrame;
    }
    if (maxDeviceArea > 1.0f) {
        area.setWH(fmin(area.width(), fMaxDeviceFrame.width()), fmin(area.height(), fMaxDeviceFrame.height()));
    }
    return area;
}

sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
    // @todo
    if (nullptr == props) {
        props = &fProps;
    }
    return this->onNewSurface(info, *props);
}

sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
    RENDER_UNIMPLEMENTED;
    return nullptr;
}

SkImageInfo SkCanvas::imageInfo() const {
    return this->onImageInfo();
}
//SkColorType ct, SkAlphaType at
SkColorType OHColorFormatToSkColorFormat(OH_Drawing_ColorFormat ohFormat) {
    switch (ohFormat) {
        case OH_Drawing_ColorFormat::COLOR_FORMAT_UNKNOWN:
            return SkColorType::kUnknown_SkColorType;
        case OH_Drawing_ColorFormat::COLOR_FORMAT_ALPHA_8:
            return SkColorType::kAlpha_8_SkColorType;
        case OH_Drawing_ColorFormat::COLOR_FORMAT_RGB_565:
            return SkColorType::kRGB_565_SkColorType;
        case OH_Drawing_ColorFormat::COLOR_FORMAT_ARGB_4444:
            return SkColorType::kARGB_4444_SkColorType;
        case OH_Drawing_ColorFormat::COLOR_FORMAT_RGBA_8888:
            return SkColorType::kRGBA_8888_SkColorType;
        case OH_Drawing_ColorFormat::COLOR_FORMAT_BGRA_8888:
            return SkColorType::kBGRA_8888_SkColorType;
        default:
            return SkColorType::kUnknown_SkColorType;;
    }
}

SkImageInfo SkCanvas::onImageInfo() const {
    // may get worng value
    RENDER_UNIMPLEMENTED;
    OH_Drawing_Image_Info ohInfo;
    auto errCode = OH_Drawing_CanvasGetImageInfo(fDrawingCanvas, &ohInfo);
    if (errCode != OH_DRAWING_SUCCESS) {
        SkDebugf("SkCanvas::imageInfo, failed to get imageInfo, oh error code: %d\n", errCode);
        return {};
    }
//    OH_Drawing_ColorFormat colorType;
//    /** storage for alpha formats */
//    OH_Drawing_AlphaFormat alphaType;
    static_assert(OH_Drawing_AlphaFormat::ALPHA_FORMAT_UNKNOWN == (int)SkAlphaType::kUnknown_SkAlphaType, "AlphaType is not equal!");
    static_assert(OH_Drawing_AlphaFormat::ALPHA_FORMAT_OPAQUE == (int)SkAlphaType::kOpaque_SkAlphaType, "AlphaType is not equal!");
    static_assert(OH_Drawing_AlphaFormat::ALPHA_FORMAT_PREMUL == (int)SkAlphaType::kPremul_SkAlphaType, "AlphaType is not equal!");
    static_assert(OH_Drawing_AlphaFormat::ALPHA_FORMAT_UNPREMUL == (int)SkAlphaType::kUnpremul_SkAlphaType, "AlphaType is not equal!");
    return SkImageInfo::Make(ohInfo.width, ohInfo.height, OHColorFormatToSkColorFormat(ohInfo.colorType), (SkAlphaType)ohInfo.alphaType); 
}

bool SkCanvas::getProps(SkSurfaceProps* props) const {
    return this->onGetProps(props, /*top=*/false);
}

SkSurfaceProps SkCanvas::getBaseProps() const {
    SkSurfaceProps props;
    this->onGetProps(&props, /*top=*/false);
    return props;
}

SkSurfaceProps SkCanvas::getTopProps() const {
    SkSurfaceProps props;
    this->onGetProps(&props, /*top=*/true);
    return props;
}

bool SkCanvas::onGetProps(SkSurfaceProps* props, bool top) const {
    RENDER_UNIMPLEMENTED;
//     if (props) {
//         *props = top ? topDevice()->surfaceProps() : fProps;
//     }
    return false;
}

bool SkCanvas::peekPixels(SkPixmap* pmap) {
    return this->onPeekPixels(pmap);
}

bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
    RENDER_UNIMPLEMENTED;
    return false;
}

void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
    RENDER_UNIMPLEMENTED;
//     SkPixmap pmap;
//     if (!this->onAccessTopLayerPixels(&pmap)) {
//         return nullptr;
//     }
//     if (info) {
//         *info = pmap.info();
//     }
//     if (rowBytes) {
//         *rowBytes = pmap.rowBytes();
//     }
//     if (origin) {
//         // If the caller requested the origin, they presumably are expecting the returned pixels to
//         // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's
//         // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix
//         // instead of an origin, just don't expose the pixels in that case. Note that this means
//         // that layers with complex coordinate spaces can still report their pixels if the caller
//         // does not ask for the origin (e.g. just to dump its output to a file, etc).
//         if (this->topDevice()->isPixelAlignedToGlobal()) {
//             *origin = this->topDevice()->getOrigin();
//         } else {
//             return nullptr;
//         }
//     }
//     return pmap.writable_addr();
    return nullptr;
}

bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
    RENDER_UNIMPLEMENTED;
    return false;
}

/////////////////////////////////////////////////////////////////////////////

void SkCanvas::translate(SkScalar dx, SkScalar dy) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(translate, dx, dy);

    if (dx || dy) {
        fCTM.preTranslate(dx, dy);
        fInvCTM.postTranslate(-dx, -dy);
        OH_Drawing_CanvasTranslate(fDrawingCanvas, dx, dy);
    }
}

void SkCanvas::scale(SkScalar sx, SkScalar sy) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(scale, sx, sy);

    if (sx != 1 || sy != 1) {
        fCTM.preScale(sx, sy);
        if (sx != 0 || sy != 0) {
            fInvCTM.postScale(1.0 / sx, 1.0 / sy);
        } else {
            fNotInv = true;
        }
        OH_Drawing_CanvasScale(fDrawingCanvas, sx, sy);
    }
}

void SkCanvas::rotate(SkScalar degrees) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(rotate, degrees);

    fCTM.preRotate(degrees);
    fInvCTM.postRotate(-degrees);
    OH_Drawing_CanvasRotate(fDrawingCanvas, degrees, 0.f, 0.f);
}

void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(rotate, degrees, px, py);

    fCTM.preRotate(degrees, px, py);
    fInvCTM.postRotate(-degrees, px, py);
    OH_Drawing_CanvasRotate(fDrawingCanvas, degrees, px, py);
}

void SkCanvas::skew(SkScalar sx, SkScalar sy) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(skew, sx, sy);

    fCTM.preSkew(sx, sy);
    fInvCTM.postSkew(-sx, -sy);
    OH_Drawing_CanvasSkew(fDrawingCanvas, sx, sy);
}

void SkCanvas::concat(const SkMatrix& matrix) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(concat, matrix);

    fCTM.preConcat(matrix);
    SkMatrix inv;
    if (matrix.invert(&inv)) {
        fInvCTM.postConcat(inv);
    } else {
        fNotInv = true;
    }
    OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, matrix);
}

void SkCanvas::internalConcat44(const SkM44& m) {
//     this->checkForDeferredSave();

//     fMCRec->fMatrix.preConcat(m);

//     this->topDevice()->setGlobalCTM(fMCRec->fMatrix);
}

void SkCanvas::concat(const SkM44& m) {
    concat(m.asM33());
//    OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, m.asM33());
}

void SkCanvas::internalSetMatrix(const SkM44& m) {
//     fMCRec->fMatrix = m;
}

void SkCanvas::setMatrix(const SkMatrix& matrix) {
//    HANDLE_SUBCANVAS_DRAW_WITHOUT_RETURN(setMatrix, matrix);
    if (fSubCanvas) {
        fSubCanvas->setMatrix(matrix);
        if (fSubCanvas->getSaveCount() == 1) {
            // if it is in save/restore, father canvas will not need reset.
            fPendingSubCanvasShouldReset = true;
        }
        return;
    }
    // Since RenderNodes are not isolated by Picture, calling setMatrix modifies the global matrix, causing the origin
    // to reset to the screen's top-left rather than the component's top-left. So we avoid to use
    // OH_Drawing_CanvasSetMatrix.
    if (fNotInv) {
        OH_Drawing_CanvasSetMatrix(fDrawingCanvas, matrix);
    } else {
        OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, fInvCTM);
        OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, matrix);
    }
    fCTM = matrix;
    SkMatrix inv;
    if (matrix.invert(&inv)) {
        fInvCTM = inv;
        fNotInv = false;
    } else {
        fNotInv = true;
    }
}

void SkCanvas::setMatrix(const SkM44& m) {
    setMatrix(m.asM33());
//    OH_Drawing_CanvasSetMatrix(fDrawingCanvas, m.asM33());
}

void SkCanvas::resetMatrix() {
//    HANDLE_SUBCANVAS_DRAW_WITHOUT_RETURN(resetMatrix);
    if (fSubCanvas) {
        fSubCanvas->resetMatrix();
        if (fSubCanvas->getSaveCount() == 1) {
            fPendingSubCanvasShouldReset = true;
        }
        return;
    }
    
    if (fNotInv) {
        OH_Drawing_CanvasResetMatrix(fDrawingCanvas);
    } else {
        OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, fInvCTM);
    }

    fCTM = SkMatrix::I();
    fInvCTM = SkMatrix::I();
    fNotInv = false;
}

//////////////////////////////////////////////////////////////////////////////

void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
    this->onClipRect(rect, op, doAA ? kSoft_ClipEdgeStyle: kHard_ClipEdgeStyle);
}

void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(onClipRect, rect, op, edgeStyle);

    // @todo clipRect before invoking OH_Drawing
    markCullShape(rect, op);
    OH_Drawing_CanvasClipRect(fDrawingCanvas, (OH_Drawing_Rect *)&rect, (OH_Drawing_CanvasClipOp)op,
                              edgeStyle == kSoft_ClipEdgeStyle);
    return;
}

void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
    RENDER_UNIMPLEMENTED;
    // The device clip restriction is a surface-space rectangular intersection that cannot be
    // drawn outside of. The rectangle is remembered so that subsequent resetClip calls still
    // respect the restriction. Other than clip resetting, all clip operations restrict the set
    // of renderable pixels, so once set, the restriction will be respected until the canvas
    // save stack is restored past the point this function was invoked. Unfortunately, the current
    // implementation relies on the clip stack of the underyling SkDevices, which leads to some
    // awkward behavioral interactions (see skbug.com/12252).
    //
    // Namely, a canvas restore() could undo the clip restriction's rect, and if
    // setDeviceClipRestriction were called at a nested save level, there's no way to undo just the
    // prior restriction and re-apply the new one. It also only makes sense to apply to the base
    // device; any other device for a saved layer will be clipped back to the base device during its
    // matched restore. As such, we:
    // - Remember the save count that added the clip restriction and reset the rect to empty when
    //   we've restored past that point to keep our state in sync with the device's clip stack.
    // - We assert that we're on the base device when this is invoked.
    // - We assert that setDeviceClipRestriction() is only called when there was no prior
    //   restriction (cannot re-restrict, and prior state must have been reset by restoring the
    //   canvas state).
    // - Historically, the empty rect would reset the clip restriction but it only could do so
    //   partially since the device's clips wasn't adjusted. Resetting is now handled
    //   automatically via SkCanvas::restore(), so empty input rects are skipped.
//     SkASSERT(this->topDevice() == this->baseDevice()); // shouldn't be in a nested layer
//     // and shouldn't already have a restriction
//     SkASSERT(fClipRestrictionSaveCount < 0 && fClipRestrictionRect.isEmpty());
//
//     if (fClipRestrictionSaveCount < 0 && !rect.isEmpty()) {
//         fClipRestrictionRect = rect;
//         fClipRestrictionSaveCount = this->getSaveCount();
//
//         // A non-empty clip restriction immediately applies an intersection op (ignoring the ctm).
//         // so we have to resolve the save.
//         this->checkForDeferredSave();
//         AutoUpdateQRBounds aqr(this);
//         // Use clipRegion() since that operates in canvas-space, whereas clipRect() would apply the
//         // device's current transform first.
//         this->topDevice()->clipRegion(SkRegion(rect), SkClipOp::kIntersect);
//     }
}

void SkCanvas::internal_private_resetClip() {
    RENDER_UNIMPLEMENTED;
//     this->checkForDeferredSave();
//     this->onResetClip();
}

void SkCanvas::onResetClip() {
    RENDER_UNIMPLEMENTED;
//     SkIRect deviceRestriction = this->topDevice()->imageInfo().bounds();
//     if (fClipRestrictionSaveCount >= 0 && this->topDevice() == this->baseDevice()) {
//         // Respect the device clip restriction when resetting the clip if we're on the base device.
//         // If we're not on the base device, then the "reset" applies to the top device's clip stack,
//         // and the clip restriction will be respected automatically during a restore of the layer.
//         if (!deviceRestriction.intersect(fClipRestrictionRect)) {
//             deviceRestriction = SkIRect::MakeEmpty();
//         }
//     }
//
//     AutoUpdateQRBounds aqr(this);
//     this->topDevice()->replaceClip(deviceRestriction);
}

void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
    ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
    if (rrect.isRect()) {
        this->onClipRect(rrect.getBounds(), op, edgeStyle);
    } else {
        this->onClipRRect(rrect, op, edgeStyle);
    }
}

void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(onClipRRect, rrect, op, edgeStyle);

    bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
    markCullShape(rrect, op);
    OH_Drawing_RoundRect* oh_rrect = rrect.buildOHRRect();
    OH_Drawing_CanvasClipRoundRect(fDrawingCanvas, oh_rrect, (OH_Drawing_CanvasClipOp)op, isAA);
    OH_Drawing_RoundRectDestroy(oh_rrect);
}

void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
//    this->checkForDeferredSave();
    ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;

     if (!path.isInverseFillType() && fCTM.rectStaysRect()) {
         SkRect r;
         if (path.isRect(&r)) {
             this->onClipRect(r, op, edgeStyle);
             return;
         }
         SkRRect rrect;
         if (path.isOval(&r)) {
             rrect.setOval(r);
             this->onClipRRect(rrect, op, edgeStyle);
             return;
         }
         if (path.isRRect(&rrect)) {
             this->onClipRRect(rrect, op, edgeStyle);
             return;
         }
     }

    this->onClipPath(path, op, edgeStyle);
}

void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
    HANDLE_SUBCANVAS_DRAW_WITHOUT_CREATE(onClipPath, path, op, edgeStyle);

    bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
    markCullShape(path, op);
    OH_Drawing_CanvasClipPath(fDrawingCanvas, path, (OH_Drawing_CanvasClipOp)op, isAA);
//    AutoUpdateQRBounds aqr(this);
//     this->topDevice()->clipPath(path, op, isAA);
}

void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) {
    RENDER_UNIMPLEMENTED;
//     if (sh) {
//         if (sh->isOpaque()) {
//             if (op == SkClipOp::kIntersect) {
//                 // we don't occlude anything, so skip this call
//             } else {
//                 SkASSERT(op == SkClipOp::kDifference);
//                 // we occlude everything, so set the clip to empty
//                 this->clipRect({0,0,0,0});
//             }
//         } else {
//             this->checkForDeferredSave();
//             this->onClipShader(std::move(sh), op);
//         }
//     }
}

void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) {
    RENDER_UNIMPLEMENTED;
//     AutoUpdateQRBounds aqr(this);
//     this->topDevice()->clipShader(sh, op);
}

void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
//     this->checkForDeferredSave();
    this->onClipRegion(rgn, op);
}

void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
    // @todo
    AutoUpdateQRBounds aqr(this);
    RENDER_UNIMPLEMENTED;
//     this->topDevice()->clipRegion(rgn, op);
}

void SkCanvas::validateClip() const {
#ifdef SK_DEBUG
    SkRect tmp = this->computeDeviceClipBounds();
    if (this->isClipEmpty()) {
        SkASSERT(fQuickRejectBounds.isEmpty());
    } else {
        SkASSERT(tmp == fQuickRejectBounds);
    }
#endif
}

bool SkCanvas::androidFramework_isClipAA() const {
    RENDER_UNIMPLEMENTED;
    return false;
//     return this->topDevice()->onClipIsAA();
}

void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) {
    RENDER_UNIMPLEMENTED;
//     rgn->setEmpty();
//     SkBaseDevice* device = this->topDevice();
//     if (device && device->isPixelAlignedToGlobal()) {
//         device->onAsRgnClip(rgn);
//         SkIPoint origin = device->getOrigin();
//         if (origin.x() | origin.y()) {
//             rgn->translate(origin.x(), origin.y());
//         }
//     }
}

///////////////////////////////////////////////////////////////////////////////

bool SkCanvas::isClipEmpty() const {
    bool ret = false;
    OH_Drawing_CanvasIsClipEmpty(fDrawingCanvas, &ret);
    return ret;
}

bool SkCanvas::isClipRect() const {
    RENDER_UNIMPLEMENTED;
    return false;
//     return this->topDevice()->onGetClipType() == SkBaseDevice::ClipType::kRect;
}

bool SkCanvas::quickReject(const SkRect& src) const {
    RENDER_UNIMPLEMENTED;
    return false;
// #ifdef SK_DEBUG
//     // Verify that fQuickRejectBounds are set properly.
//     this->validateClip();
// #endif
// 
//     SkRect devRect = SkMatrixPriv::MapRect(fMCRec->fMatrix, src);
//     return !devRect.isFinite() || !devRect.intersects(fQuickRejectBounds);
}

bool SkCanvas::quickReject(const SkPath& path) const {
    RENDER_UNIMPLEMENTED;
    return false;
//     return path.isEmpty() || this->quickReject(path.getBounds());
}

bool SkCanvas::internalQuickReject(const SkRect& bounds, const SkPaint& paint,
                                   const SkMatrix* matrix) {
    RENDER_UNIMPLEMENTED;
    return false;
    if (!bounds.isFinite() || paint.nothingToDraw()) {
        return true;
    }

    if (paint.canComputeFastBounds()) {
        SkRect tmp = matrix ? matrix->mapRect(bounds) : bounds;
        return this->quickReject(paint.computeFastBounds(tmp, &tmp));
    }

    return false;
}


SkRect SkCanvas::getLocalClipBounds(bool needReal) const {
    SkRect rect = SkRect::MakeEmpty();
    SkRect nowClip = computeDeviceClipBounds(false, needReal);
    if (fSubCanvas && !fSubPicIsPlayback) {
        SkRect subRect = fSubCanvas->computeDeviceClipBounds(false, needReal);
        nowClip.join(subRect);
    }
    if (!fNotInv) {
        fInvCTM.mapRect(&rect, nowClip);
    }
    return rect;
//        OH_Drawing_CanvasGetLocalClipBounds(fDrawingCanvas, (OH_Drawing_Rect *)&rect);
//    SkIRect ibounds = this->getDeviceClipBounds();
//    if (ibounds.isEmpty()) {
//        return SkRect::MakeEmpty();
//    }
//
//    SkMatrix inverse;
//    // if we can't invert the CTM, we can't return local clip bounds
////     if (!fMCRec->fMatrix.asM33().invert(&inverse)) {
////         return SkRect::MakeEmpty();
////     }
//
//    SkRect bounds;
//    // adjust it outwards in case we are antialiasing
//    const int margin = 1;
//
//    SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin));
//    inverse.mapRect(&bounds, r);
//    return bounds;
}

SkClipShape SkCanvas::getLocalClipShape() const {
    SkClipShape nowClipShape = computeDeviceClipShape(false);
    if (!fNotInv) {
        if (std::holds_alternative<SkRect>(nowClipShape)) {
            SkRect rect;
            fInvCTM.mapRect(&rect, std::get<SkRect>(nowClipShape));
            return rect;
        } else if (std::holds_alternative<SkRRect>(nowClipShape)) {
            SkRRect rrect = std::get<SkRRect>(nowClipShape);
            SkRRect mapped;
            if (rrect.transform(fInvCTM, &mapped)) {
                return mapped;
            } else {
                SkPath path;
                path.addRRect(rrect);
                path.transform(fInvCTM);
                return path;
            }
        } else if (std::holds_alternative<SkPath>(nowClipShape)) {
            SkPath path = std::get<SkPath>(nowClipShape);
            SkPath mapped;
            path.transform(fInvCTM, &mapped);
            return mapped;
        } else {
            return nowClipShape;
        }
    }
    return std::monostate();
//    return SkRect::MakeEmpty();
}

SkIRect SkCanvas::getDeviceClipBounds(bool needReal) const {
    SkRect nowClip = computeDeviceClipBounds(false, needReal);
    if (fSubCanvas && !fSubPicIsPlayback) {
        SkRect subRect = fSubCanvas->computeDeviceClipBounds(false, needReal);
        nowClip.join(subRect);
    }
    return nowClip.roundOut();
//    SkRect local_clip = getLocalClipBounds();
//    SkDebugf("get local clip %f %f %f %f", local_clip.x(), local_clip.y(), local_clip.width(), local_clip.height());
//    SkMatrix matrix = getTotalMatrix();
//    
//    SkRect device_clip;
//    matrix.mapRect(&device_clip, local_clip);
//    return device_clip.roundOut();
//    return this->computeDeviceClipBounds(/*outsetForAA=*/false).roundOut();
}

SkClipShape SkCanvas::getDeviceClipShape() const {
    return computeDeviceClipShape(false);
}


SkRect SkCanvas::computeDeviceClipBounds(bool outsetForAA, bool needReal) const {
    SkRect nowClip = SkRect::MakeEmpty();
    if (fCullRect.has_value()) {
        nowClip = fCullRect.value();
    }
    if (!needReal && !fRecordCull.isEmpty()) {
        if (nowClip.isEmpty()) {
            nowClip = fRecordCull;
        } else {
            if (!nowClip.intersect(fRecordCull)) {
                nowClip = SkRect::MakeEmpty();
            }
        }
    }
    if (outsetForAA) {
        // Expand bounds out by 1 in case we are anti-aliasing.  We store the
        // bounds as floats to enable a faster quick reject implementation.
        nowClip.outset(1.f, 1.f);
    }
    nowClip.makeSorted();
    return nowClip;
//     const SkBaseDevice* dev = this->topDevice();
//     if (dev->onGetClipType() == SkBaseDevice::ClipType::kEmpty) {
//         return SkRect::MakeEmpty();
//     } else {
//         SkRect devClipBounds =
//                 SkMatrixPriv::MapRect(dev->deviceToGlobal(), SkRect::Make(dev->devClipBounds()));
//         if (outsetForAA) {
//             // Expand bounds out by 1 in case we are anti-aliasing.  We store the
//             // bounds as floats to enable a faster quick reject implementation.
//             devClipBounds.outset(1.f, 1.f);
//         }
//         return devClipBounds;
//     }
}

SkClipShape SkCanvas::computeDeviceClipShape(bool outsetForAA) const {
    SkClipShape clipShape = fClipShape;

    if (outsetForAA) {
        if (std::holds_alternative<SkRect>(clipShape)) {
            SkRect r = std::get<SkRect>(clipShape);
            r.outset(1.f, 1.f);
            r.sort();
            clipShape = r;
        } else if (std::holds_alternative<SkRRect>(clipShape)) {
            SkRRect rr = std::get<SkRRect>(clipShape);
            rr.outset(1.f, 1.f);
            clipShape = rr;
        } else {
            // dose SkPath need AA?
        }
    }

    if (std::holds_alternative<SkRect>(clipShape)) {
        SkRect r = std::get<SkRect>(clipShape);
        if (!r.isSorted()) {
            r.sort();
            clipShape = r;
        }
    }

    return clipShape;
}

///////////////////////////////////////////////////////////////////////

SkMatrix SkCanvas::getTotalMatrix() const {
    SkMatrix matrix = fCTM;
    if (fSubCanvas && !fSubPicIsPlayback) {
        matrix = matrix * fSubCanvas->getTotalMatrix();
    }
    return matrix;
}

// private function
SkMatrix SkCanvas::getInvMatrix() const {
    SkMatrix matrix = fInvCTM;
    return matrix;
}

SkM44 SkCanvas::getLocalToDevice() const {
//    OH_Drawing_CanvasGetTotalMatrix(fDrawingCanvas, matrix);
//    matrix.fillWithOHMatrix();
    return SkM44(getTotalMatrix());
}

GrRecordingContext* SkCanvas::recordingContext() {
    RENDER_UNIMPLEMENTED;
    return nullptr;
// #if defined(SK_GANESH)
//     if (auto gpuDevice = this->topDevice()->asGaneshDevice()) {
//         return gpuDevice->recordingContext();
//     }
// #endif
//
//     return nullptr;
}

skgpu::graphite::Recorder* SkCanvas::recorder() {
    RENDER_UNIMPLEMENTED;
    return nullptr;
// #if defined(SK_GRAPHITE)
//     if (auto graphiteDevice = this->topDevice()->asGraphiteDevice()) {
//         return graphiteDevice->recorder();
//     }
// #endif
//
//     return nullptr;
}


void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
                          const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (outer.isEmpty()) {
        return;
    }
    if (inner.isEmpty()) {
        this->drawRRect(outer, paint);
        return;
    }

    // We don't have this method (yet), but technically this is what we should
    // be able to return ...
    // if (!outer.contains(inner))) {
    //
    // For now at least check for containment of bounds
    if (!outer.getBounds().contains(inner.getBounds())) {
        return;
    }

    this->onDrawDRRect(outer, inner, paint);
}

void SkCanvas::drawPaint(const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawPaint(paint);
}

void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    // @ok
    // To avoid redundant logic in our culling code and various backends, we always sort rects
    // before passing them along.
    this->onDrawRect(r.makeSorted(), paint);
}

void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawBehind(paint);
}

void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
    // @ok no need
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (region.isEmpty()) {
        return;
    }

    if (region.isRect()) {
        return this->drawIRect(region.getBounds(), paint);
    }

    this->onDrawRegion(region, paint);
}

void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    // To avoid redundant logic in our culling code and various backends, we always sort rects
    // before passing them along.
    this->onDrawOval(r.makeSorted(), paint);
}

void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawRRect(rrect, paint);
}

void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawPoints(mode, count, pts, paint);
}

void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode,
                            const SkPaint& paint) {
    // @ok
    this->drawVertices(vertices.get(), mode, paint);
}

void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    RETURN_ON_NULL(vertices);

    float drawRatio = 0.f;
    SkPaint fillPaint = paint;
    fillPaint.setStyle(SkPaint::Style::kFill_Style);
    bool intersect = markDrawBounds(vertices->bounds(), &fillPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(drawVertices, vertices, mode, paint);
    if (fEnableHash) {
        updateHashWithState();
        fHasher.pushArray((const char *)vertices, vertices->approximateSize());
        fHasher.push(mode);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        float costPerPixel =
            paint.computePerPixelCost(vertices->bounds());
        fDrawCost +=
            costPerPixel * vertices->bounds().height() * vertices->bounds().width() * drawRatio + doRealClipCost();
    }

    OH_Drawing_CanvasDrawVertices(fDrawingCanvas,
                                  (OH_Drawing_VertexMode)vertices->vMode(),
                                  vertices->vCount(),
                                  (const OH_Drawing_Point2D*)vertices->positions(),
                                  (const OH_Drawing_Point2D*)vertices->texCoords(),
                                  vertices->colors(),
                                  vertices->iCount(),
                                  vertices->indices(),
                                  (OH_Drawing_BlendMode)mode);

    if (OH_Drawing_ErrorCodeGet() != OH_DRAWING_SUCCESS) {
        SkDebugf("SkCanvas::drawVertices ERROR!");
    }
    // We expect fans to be converted to triangles when building or deserializing SkVertices.
//     SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode);

//#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
//    // Preserve legacy behavior for Android: ignore the SkShader if there are no texCoords present
//    if (paint.getShader() && !vertices->priv().hasTexCoords()) {
//        SkPaint noShaderPaint(paint);
//        noShaderPaint.setShader(nullptr);
//        this->onDrawVerticesObject(vertices, mode, noShaderPaint);
//        return;
//    }
//#endif
//    this->onDrawVerticesObject(vertices, mode, paint);
}

#ifdef SK_ENABLE_SKSL
void SkCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    RETURN_ON_FALSE(mesh.isValid());
    if (!blender) {
        blender = SkBlender::Mode(SkBlendMode::kModulate);
    }
    this->onDrawMesh(mesh, std::move(blender), paint);
}
#endif

void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawPath(path, paint);
}

// Returns true if the rect can be "filled" : non-empty and finite
static bool fillable(const SkRect& r) {
    SkScalar w = r.width();
    SkScalar h = r.height();
    return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0;
}

static SkPaint clean_paint_for_lattice(const SkPaint* paint) {
    // @todo
    SkPaint cleaned;
    if (paint) {
        cleaned = *paint;
        cleaned.setMaskFilter(nullptr);
        cleaned.setAntiAlias(false);
    }
    return cleaned;
}

void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
                             SkFilterMode filter, const SkPaint* paint) {
    RETURN_ON_NULL(image);
    // @ok no need

    const int xdivs[] = {center.fLeft, center.fRight};
    const int ydivs[] = {center.fTop, center.fBottom};

    Lattice lat;
    lat.fXDivs = xdivs;
    lat.fYDivs = ydivs;
    lat.fRectTypes = nullptr;
    lat.fXCount = lat.fYCount = 2;
    lat.fBounds = nullptr;
    lat.fColors = nullptr;
    this->drawImageLattice(image, lat, dst, filter, paint);
}

void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
                                SkFilterMode filter, const SkPaint* paint) {
    RENDER_UNIMPLEMENTED;
    return;
//     TRACE_EVENT0("skia", TRACE_FUNC);
//     RETURN_ON_NULL(image);
//     if (dst.isEmpty()) {
//         return;
//     }
//
//     SkIRect bounds;
//     Lattice latticePlusBounds = lattice;
//     if (!latticePlusBounds.fBounds) {
//         bounds = SkIRect::MakeWH(image->width(), image->height());
//         latticePlusBounds.fBounds = &bounds;
//     }
//
//     SkPaint latticePaint = clean_paint_for_lattice(paint);
//     if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
//         this->onDrawImageLattice2(image, latticePlusBounds, dst, filter, &latticePaint);
//     } else {
//         this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst,
//                             SkSamplingOptions(filter), &latticePaint, kStrict_SrcRectConstraint);
//     }
}

void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
                         const SkColor colors[], int count, SkBlendMode mode,
                         const SkSamplingOptions& sampling, const SkRect* cull,
                         const SkPaint* paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    RETURN_ON_NULL(atlas);
    if (count <= 0) {
        return;
    }
    SkASSERT(atlas);
    SkASSERT(tex);
    this->onDrawAtlas2(atlas, xform, tex, colors, count, mode, sampling, cull, paint);
}

void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (key) {
        this->onDrawAnnotation(rect, key, value);
    }
}

void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawShadowRec(path, rec);
}

static SkScalar compute_z(SkScalar x, SkScalar y, const SkPoint3& params) {
    return x*params.fX + y*params.fY + params.fZ;
}

void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
    // We don't test quickReject because the shadow outsets the path's bounds.
//     // TODO(michaelludwig): Is it worth calling SkDrawShadowMetrics::GetLocalBounds here?
//     if (!this->predrawNotify()) {
//         return;
//     }
    SkRect bounds;
    SkDrawShadowMetrics::GetLocalBounds(path, rec, fCTM, &bounds);
    bool intersect = markDrawBounds(bounds, nullptr);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawShadowRec, path, rec);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(path.hash());
        fHasher.push(rec);
    }
    
    if (OHRenderNode::gEnableRasterCache) {
        // shadow will change when matrix is changed, so it cannot be drawn into raster cache.
        fForbidRasterCache = true;
    }

    OH_Drawing_CanvasDrawShadow(fDrawingCanvas, path, *(OH_Drawing_Point3D *)&rec.fZPlaneParams,
                                *(OH_Drawing_Point3D *)&rec.fLightPos, rec.fLightRadius, rec.fAmbientColor,
                                rec.fSpotColor, OHShadowFlags(rec.fFlags));
}

void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
                                           QuadAAFlags aaFlags, const SkColor4f& color,
                                           SkBlendMode mode) {
    RENDER_UNIMPLEMENTED;
//     TRACE_EVENT0("skia", TRACE_FUNC);
//     // Make sure the rect is sorted before passing it along
//     this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode);
}

void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt,
                                               const SkPoint dstClips[],
                                               const SkMatrix preViewMatrices[],
                                               const SkSamplingOptions& sampling,
                                               const SkPaint* paint,
                                               SrcRectConstraint constraint) {
    RENDER_UNIMPLEMENTED;
//     TRACE_EVENT0("skia", TRACE_FUNC);
//     this->onDrawEdgeAAImageSet2(imageSet, cnt, dstClips, preViewMatrices, sampling, paint,
//                                 constraint);
}

//////////////////////////////////////////////////////////////////////////////
//  These are the virtual drawing methods
//////////////////////////////////////////////////////////////////////////////

void SkCanvas::onDiscard() {
    RENDER_UNIMPLEMENTED;
//     if (fSurfaceBase) {
//         sk_ignore_unused_variable(fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode));
//     }
}

void SkCanvas::onDrawPaint(const SkPaint& paint) {
    bool intersect = markDrawBounds(std::nullopt, &paint);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawPaint, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        fHasher.push(GetClipShapeHash(fClipShape));
        fHasher.push(GetClipShapeHash(fPendingDifferenceShape));
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && (!fCullRect.has_value() || !fCullRect->isEmpty())) {
        SkRect area = maxDrawArea();
        SkRect localArea;
        if (!fNotInv) {
            fInvCTM.mapRect(&localArea, area);
        }
        fDrawCost +=
            area.width() * area.height() * paint.computePerPixelCost(localArea) + doRealClipCost();
    }
    paint.updateOHPainter();
    OH_Drawing_CanvasDrawBackground(fDrawingCanvas, paint);
}

void SkCanvas::internalDrawPaint(const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
    // drawPaint does not call internalQuickReject() because computing its geometry is not free
    // (see getLocalClipBounds(), and the two conditions below are sufficient.
    if (paint.nothingToDraw() || this->isClipEmpty()) {
        return;
    }

    auto layer = this->aboutToDraw(this, paint, nullptr, CheckForOverwrite::kYes);
    if (layer) {
//         this->topDevice()->drawPaint(layer->paint());
    }
}

void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
                            const SkPaint& paint) {
//    HANDLE_SUBCANVAS_DRAW(onDrawPoints, mode, count, pts, paint);

    // Enforce paint style matches implicit behavior of drawPoints
    SkPaint strokePaint = paint;
    strokePaint.setStyle(SkPaint::kStroke_Style);

    SkRect dst;
    dst.setBounds(pts, count);
    // Pad the bounding box a little to make sure hairline points' bounds aren't empty.
    SkScalar stroke = std::max(strokePaint.getStrokeWidth(), 0.01f);
    if (count == 1 || mode == PointMode::kPoints_PointMode) {
        dst.outset(stroke/2, stroke/2);
    } else {
        SkScalar miter = strokePaint.getStrokeMiter();
        if (count == 2 || mode == PointMode::kLines_PointMode) {
            // this don't need miter limit.
            strokePaint.setStrokeMiter(1);
        }
        SkScalar radius = SkStrokeRec::GetInflationRadius(strokePaint, strokePaint.getStyle());
        strokePaint.setStrokeMiter(miter);
        dst.outset(radius, radius);
    }

    // Stroke mode may cause outer boundary expansion
    SkPaint fillPaint = paint;
    fillPaint.setStyle(SkPaint::kFill_Style);
    
    float drawRatio = 0.f;
    bool intersect = markDrawBounds(dst, &fillPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawPoints, mode, count, pts, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(mode);
        fHasher.pushArray(pts, count);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        float costPerPixel = paint.computePerPixelCost(dst);
        fDrawCost += count * stroke * stroke * drawRatio * costPerPixel + doRealClipCost();
    }

    attachPaint(strokePaint);
    OH_Drawing_CanvasDrawPoints(fDrawingCanvas, (OH_Drawing_PointMode)mode, count, (OH_Drawing_Point2D *)pts);
    detachPaint();
    
//     if ((long)count <= 0 || paint.nothingToDraw()) {
//         return;
//     }
//     SkASSERT(pts != nullptr);
//
//     SkRect bounds;
//     // Compute bounds from points (common for drawing a single line)
//     if (count == 2) {
//         bounds.set(pts[0], pts[1]);
//     } else {
//         bounds.setBounds(pts, SkToInt(count));
//     }
//
//     // Enforce paint style matches implicit behavior of drawPoints
//     SkPaint strokePaint = paint;
//     strokePaint.setStyle(SkPaint::kStroke_Style);
//     if (this->internalQuickReject(bounds, strokePaint)) {
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, strokePaint, &bounds);
//     if (layer) {
// //         this->topDevice()->drawPoints(mode, count, pts, layer->paint());
//     }
}

void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
    float drawRatio = 0.f;
    SkPaint boundPaint = paint;
    boundPaint.setStrokeCap(SkPaint::Cap::kButt_Cap);
    boundPaint.setStrokeJoin(SkPaint::Join::kRound_Join);
    bool intersect = markDrawBounds(r, &boundPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawRect, r, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(r);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        float costPerPixel = paint.computePerPixelCost(r);
        if (paint.getStyle() == SkPaint::Style::kStroke_Style) {
            drawRatio = drawRatio / (((r.width() + paint.getStrokeWidth()) * (r.height() + paint.getStrokeWidth())) /
                                     (r.width() * r.height()));
        }
        fDrawCost += getAreaForPaint(r, paint, SkRectType::kRect) * drawRatio * costPerPixel + doRealClipCost();
    }
    attachPaint(paint);
    OH_Drawing_CanvasDrawRect(fDrawingCanvas, (const OH_Drawing_Rect *)&r);
    detachPaint();
}

void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
    // @todo
    const SkRect bounds = SkRect::Make(region.getBounds());
    if (this->internalQuickReject(bounds, paint)) {
        return;
    }

    auto layer = this->aboutToDraw(this, paint, &bounds);
    if (layer) {
//         this->topDevice()->drawRegion(region, layer->paint());
    }
}

void SkCanvas::onDrawBehind(const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     SkBaseDevice* dev = this->topDevice();
//     if (!dev) {
//         return;
//     }
//
//     SkIRect bounds;
//     SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
//     for (;;) {
//         const MCRec* rec = (const MCRec*)iter.prev();
//         if (!rec) {
//             return; // no backimages, so nothing to draw
//         }
//         if (rec->fBackImage) {
//             // drawBehind should only have been called when the saveBehind record is active;
//             // if this fails, it means a real saveLayer was made w/o being restored first.
//             SkASSERT(dev == rec->fDevice);
//             bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
//                                        rec->fBackImage->fImage->width(),
//                                        rec->fBackImage->fImage->height());
//             break;
//         }
//     }
//
//     // The backimage location (and thus bounds) were defined in the device's space, so mark it
//     // as a clip. We use a clip instead of just drawing a rect in case the paint has an image
//     // filter on it (which is applied before any auto-layer so the filter is clipped).
//     dev->save();
//     {
//         // We also have to temporarily whack the device matrix since clipRegion is affected by the
//         // global-to-device matrix and clipRect is affected by the local-to-device.
//         SkAutoDeviceTransformRestore adtr(dev, SkMatrix::I());
//         dev->clipRect(SkRect::Make(bounds), SkClipOp::kIntersect, /* aa */ false);
//         // ~adtr will reset the local-to-device matrix so that drawPaint() shades correctly.
//     }
//
//     auto layer = this->aboutToDraw(this, paint);
//     if (layer) {
//         this->topDevice()->drawPaint(layer->paint());
//     }
//
//     dev->restore(fMCRec->fMatrix);
}

void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {

    SkASSERT(oval.isSorted());
    float drawRatio = 0.f;
    SkPaint boundPaint = paint;
    boundPaint.setStrokeCap(SkPaint::Cap::kButt_Cap);
    boundPaint.setStrokeJoin(SkPaint::Join::kRound_Join);
    bool intersect = markDrawBounds(oval, &boundPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawOval, oval, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(oval);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        if (paint.getStyle() == SkPaint::Style::kStroke_Style) {
            drawRatio =
                drawRatio / (((oval.width() + paint.getStrokeWidth()) * (oval.height() + paint.getStrokeWidth())) /
                             (oval.width() * oval.height()));
        }
        float costPerPixel = paint.computePerPixelCost(oval);
        fDrawCost += getAreaForPaint(oval, paint, SkRectType::kOval) * drawRatio * costPerPixel + doRealClipCost();
    }
    attachPaint(paint);
    OH_Drawing_CanvasDrawOval(fDrawingCanvas, (const OH_Drawing_Rect *)&oval);
    detachPaint();
}

void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
                         SkScalar sweepAngle, bool useCenter,
                         const SkPaint& paint) {

    SkASSERT(oval.isSorted());
    float drawRatio = 0.f;
    SkPaint boundPaint = paint;
    boundPaint.setStrokeJoin(SkPaint::Join::kRound_Join);
    bool intersect = markDrawBounds(oval, &boundPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawArc, oval, startAngle, sweepAngle, useCenter, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(oval);
        fHasher.push(startAngle);
        fHasher.push(sweepAngle);
        fHasher.push(useCenter);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        float costPerPixel = paint.computePerPixelCost(oval);
        float otherArea = 0.f;
        // this is not accurate
        if (paint.getStyle() == SkPaint::kStroke_Style) {
            if (useCenter) {
                otherArea = std::max(oval.width(), oval.height()) * 2 * paint.getStrokeWidth();
            }
            drawRatio =
                drawRatio / (((oval.width() + paint.getStrokeWidth()) * (oval.height() + paint.getStrokeWidth())) /
                             (oval.width() * oval.height()));
        } else if (paint.getStyle() == SkPaint::kFill_Style) {
        } else {
            if (useCenter) {
                otherArea = std::max(oval.width(), oval.height()) * paint.getStrokeWidth();
            }
        }

        fDrawCost += (getAreaForPaint(oval, paint, SkRectType::kOval) * std::min(sweepAngle / 360, 1.0f) + otherArea) *
                         drawRatio * costPerPixel +
                     doRealClipCost();
    }
    attachPaint(paint);
    OHDrawingAPI::OH_Drawing_CanvasDrawArcWithCenter(fDrawingCanvas, (const OH_Drawing_Rect *)&oval, startAngle,
                                                     sweepAngle, useCenter);
    detachPaint();
}

void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {

    const SkRect& bounds = rrect.getBounds();
    // Delegating to simpler draw operations
    if (rrect.isRect()) {
        // call the non-virtual version
        this->SkCanvas::drawRect(bounds, paint);
        return;
    } else if (rrect.isOval()) {
        // call the non-virtual version
        this->SkCanvas::drawOval(bounds, paint);
        return;
    }
    float drawRatio = 0.f;
    SkPaint boundPaint = paint;
    boundPaint.setStrokeCap(SkPaint::Cap::kButt_Cap);
    boundPaint.setStrokeJoin(SkPaint::Join::kRound_Join);
    bool intersect = markDrawBounds(rrect.rect(), &boundPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawRRect, rrect, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(rrect);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        if (paint.getStyle() == SkPaint::Style::kStroke_Style) {
            drawRatio =
                drawRatio /
                (((rrect.rect().width() + paint.getStrokeWidth()) * (rrect.rect().height() + paint.getStrokeWidth())) /
                 (rrect.rect().width() * rrect.rect().height()));
        }
        fDrawCost +=
            getAreaForPaint(rrect.rect(), paint) * drawRatio * paint.computePerPixelCost(rrect.rect()) + doRealClipCost();
    }
    auto oh_rrect = rrect.buildOHRRect();
    attachPaint(paint);
    OH_Drawing_CanvasDrawRoundRect(fDrawingCanvas, oh_rrect);
    detachPaint();
    OH_Drawing_RoundRectDestroy(oh_rrect);
}

void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {

    OH_Drawing_RoundRect* oh_outer = outer.buildOHRRect();
    OH_Drawing_RoundRect* oh_inner = inner.buildOHRRect();
    
    float drawRatio = 0.f;
    SkPaint boundPaint = paint;
    boundPaint.setStrokeCap(SkPaint::Cap::kButt_Cap);
    boundPaint.setStrokeJoin(SkPaint::Join::kRound_Join);
    bool intersect = markDrawBounds(outer.rect(), &boundPaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawDRRect, outer, inner, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(outer);
        fHasher.push(inner);
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        // clip need write stencil
        float drawArea = getAreaForPaint(outer.rect(), paint);
        if (paint.getStyle() == SkPaint::kStroke_Style) {
            drawArea += getAreaForPaint(inner.rect(), paint);
            drawRatio =
                drawRatio /
                (((outer.rect().width() + paint.getStrokeWidth()) * (outer.rect().height() + paint.getStrokeWidth())) /
                 (outer.rect().width() * outer.rect().height()));
        } else if (paint.getStyle() == SkPaint::kFill_Style) {
            if (OHDrawingAPI::support_OH_Drawing_CanvasDrawNestedRoundRect) {
                drawArea -= getAreaForPaint(inner.rect(), paint);
            } else {
                drawArea += getAreaForPaint(inner.rect(), paint);
            }
        }
        fDrawCost += drawArea * drawRatio * paint.computePerPixelCost(outer.rect()) + doRealClipCost();
    }
    attachPaint(paint);
    if (OHDrawingAPI::support_OH_Drawing_CanvasDrawNestedRoundRect) {
        OHDrawingAPI::OH_Drawing_CanvasDrawNestedRoundRect(fDrawingCanvas, oh_outer, oh_inner);
    } else {
        if (paint.getStyle() == SkPaint::Style::kStroke_Style) {
            OH_Drawing_CanvasDrawRoundRect(fDrawingCanvas, oh_outer);
            OH_Drawing_CanvasDrawRoundRect(fDrawingCanvas, oh_inner);
        } else if (paint.getStyle() == SkPaint::Style::kFill_Style) {
            OH_Drawing_CanvasClipRoundRect(fDrawingCanvas, oh_inner, OH_Drawing_CanvasClipOp::DIFFERENCE, paint.isAntiAlias());
            OH_Drawing_CanvasDrawRoundRect(fDrawingCanvas, oh_outer);
        } else {
            OH_Drawing_CanvasDrawRoundRect(fDrawingCanvas, oh_outer);
        }
    }
    detachPaint();
    OH_Drawing_RoundRectDestroy(oh_outer);
    OH_Drawing_RoundRectDestroy(oh_inner);
}

void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
    float drawRatio = 0.f;
    bool intersect = markDrawBounds(path.getBounds(), &paint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawPath, path, paint);
    if (fEnableHash) {
        fHasher.push(HASH_UNIQUE_ID);
        updateHashWithState();
        fHasher.push(path.hash());
        fHasher.push(paint.hash());
    }
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        float paintArea = 0.f;
        if (!path.isInverseFillType()) {
            SkRect r;
            SkRRect rrect;
            SkPoint point[2];
            if (path.isRect(&r)) {
                paintArea = getAreaForPaint(r, paint);
            } else if (path.isOval(&r)) {
                paintArea = getAreaForPaint(r, paint, SkRectType::kOval);
            } else if (path.isRRect(&rrect)) {
                paintArea = getAreaForPaint(rrect.rect(), paint);
            } else if (path.isLine(point)) {
                paintArea = std::sqrt((point[0].x() - point[1].x()) * (point[0].x() - point[1].x()) +
                                      (point[0].y() - point[1].y()) * (point[0].y() - point[1].y())) *
                            (paint.getStyle() != SkPaint::kFill_Style ? paint.getStrokeWidth() : 1);
            } else {
                paintArea = getAreaForPaint(path.getBounds(), paint, SkRectType::kOther);
            }
        } else {
            if (fCullRect.has_value()) {
                paintArea = fCullRect.value().width() * fCullRect.value().height();
            } else {
                paintArea = fMaxDeviceFrame.width() * fMaxDeviceFrame.height();
            }
        }
        fDrawCost += paintArea * drawRatio * paint.computePerPixelCost(path.getBounds()) + doRealClipCost();;
    }
    attachPaint(paint);
    OH_Drawing_CanvasDrawPath(fDrawingCanvas, path);
    detachPaint();
//     // @todo
//     if (!path.isFinite()) {
//         return;
//     }
//
//     const SkRect& pathBounds = path.getBounds();
//     if (!path.isInverseFillType() && this->internalQuickReject(pathBounds, paint)) {
//         return;
//     }
//     if (path.isInverseFillType() && pathBounds.width() <= 0 && pathBounds.height() <= 0) {
//         this->internalDrawPaint(paint);
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, paint, path.isInverseFillType() ? nullptr : &pathBounds);
//     if (layer) {
// //         this->topDevice()->drawPath(path, layer->paint());
//     }
}

bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h,
                                     const SkSamplingOptions& sampling, const SkPaint& paint) {
    if (!paint.getImageFilter()) {
        return false;
    }

    const SkMatrix& ctm = this->getTotalMatrix();
//     if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), sampling, paint.isAntiAlias())) {
//         return false;
//     }

    // The other paint effects need to be applied before the image filter, but the sprite draw
    // applies the filter explicitly first.
    if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) {
        return false;
    }
    // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
    // Once we can filter and the filter will return a result larger than itself, we should be
    // able to remove this constraint.
    // skbug.com/4526
    //
    SkPoint pt;
    ctm.mapXY(x, y, &pt);
    SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
    // quick bounds have been outset by 1px compared to overall device bounds, so this makes the
    // contains check equivalent to between ir and device bounds
    ir.outset(1, 1);
    return ir.contains(fQuickRejectBounds);
}

// Clean-up the paint to match the drawing semantics for drawImage et al. (skbug.com/7804).
// static SkPaint clean_paint_for_drawImage(const SkPaint* paint) {
//     SkPaint cleaned;
//     if (paint) {
//         cleaned = *paint;
//         cleaned.setStyle(SkPaint::kFill_Style);
//         cleaned.setPathEffect(nullptr);
//     }
//     return cleaned;
// }

// drawVertices fills triangles and ignores mask filter and path effect,
// so canonicalize the paint before checking quick reject.
static SkPaint clean_paint_for_drawVertices(SkPaint paint) {
    paint.setStyle(SkPaint::kFill_Style);
    paint.setMaskFilter(nullptr);
    paint.setPathEffect(nullptr);
    return paint;
}

void SkCanvas::onDrawImage2(const SkImage *image, SkScalar x, SkScalar y, const SkSamplingOptions &sampling,
                            const SkPaint *paint) {
    auto info = image->imageInfo();
    if (info.width() == 0 || info.height() == 0) {
        return;
    }
    SkRect src = {0.0f, 0.0f, 0.0f + info.width(), 0.0f + info.height()};
    SkRect dst = {x, y, x + info.width(), y + info.height()};
    float drawRatio = 0.f;
    SkPaint imagePaint;
    if (paint) {
        imagePaint = *paint;
        imagePaint.setStyle(SkPaint::Style::kFill_Style);
    }
    bool intersect = markDrawBounds(dst, &imagePaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawImage2, image, x, y, sampling, paint);
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        // default has blend.
        float costPerPixel = 1 + 1;
        if (paint) {
            // shader has no effect
            costPerPixel = paint->computePerPixelCost();
        }
        // read + cost
        fDrawCost += info.width() * info.height() * (1 + drawRatio * costPerPixel) + doRealClipCost();;
    }


    auto oh_options = OH_Drawing_SamplingOptionsCreate((OH_Drawing_FilterMode)sampling.filter,
                                                       (OH_Drawing_MipmapMode)sampling.mipmap);
    if (image->isOpaque() && imagePaint.getImageFilter() == nullptr && imagePaint.isSrcOver()) {
        imagePaint.setBlendMode(SkBlendMode::kSrc);
    }
    if (fEnableHash) {
        fHasher.push(&OH_Drawing_CanvasDrawPixelMapRect);
        updateHashWithState();
        if (image) {
            fHasher.push(as_IB(image)->hash());
        }
        fHasher.push(src);
        fHasher.push(dst);
        fHasher.push(sampling);
        fHasher.push(imagePaint.hash());
        fHasher.push(kFast_SrcRectConstraint);
    }
    attachPaint(imagePaint);
    OH_Drawing_CanvasDrawPixelMapRect(fDrawingCanvas, as_IB((SkImage *)image)->getOHPixelmap(), (OH_Drawing_Rect *)&src,
                                      (OH_Drawing_Rect *)&dst, oh_options);
    detachPaint();
    OH_Drawing_SamplingOptionsDestroy(oh_options);
}

// static SkSamplingOptions clean_sampling_for_constraint(
//         const SkSamplingOptions& sampling,
//         SkCanvas::SrcRectConstraint constraint) {
//     if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
//         if (sampling.mipmap != SkMipmapMode::kNone) {
//             return SkSamplingOptions(sampling.filter);
//         }
//         if (sampling.isAniso()) {
//             return SkSamplingOptions(SkFilterMode::kLinear);
//         }
//     }
//     return sampling;
// }

void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
                                const SkSamplingOptions& sampling, const SkPaint* paint,
                                SrcRectConstraint constraint) {
    if (dst.width() == 0 || dst.height() == 0 || src.width() == 0 || src.height() == 0) {
        return;
    }
    
    // @todo: SrcRectConstraint don't support in ohos
    float drawRatio = 0.f;
    SkPaint imagePaint;
    if (paint) {
        imagePaint = *paint;
        imagePaint.setStyle(SkPaint::Style::kFill_Style);
    }
    bool intersect = markDrawBounds(dst, &imagePaint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(onDrawImageRect2, image, src, dst, sampling, paint, constraint);
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        // default has blend.
        float costPerPixel = 1 + 1;
        int inputNum = 0;
        float scaleX = src.width() / dst.width();
        float scaleY = src.height() / dst.height();
        float maxScale = std::max(scaleX, scaleY);
        double mipLevel = std::log2(std::max(maxScale, 1.0f));
        int intLevel = static_cast<int>(std::floor(mipLevel));
        int mipWidth0 = std::max(1, (int)src.width() >> intLevel);
        int mipHeight0 = std::max(1, (int)src.height()  >> intLevel);
        int mipWidth1 = std::max(1, (int)src.width() >> (intLevel + 1));
        int mipHeight1 = std::max(1, (int)src.height() >> (intLevel + 1));
        if (sampling.mipmap == SkMipmapMode::kLinear) {
            inputNum = mipWidth0 * mipHeight0 + mipWidth1 * mipHeight1;
        } else if (sampling.mipmap == SkMipmapMode::kNearest) {
            if (mipWidth0 - dst.width() < dst.width() - mipWidth1) {
                inputNum = mipWidth0 * mipHeight0;
            } else {
                inputNum = mipWidth1 * mipHeight1;
            }
        } else {
            inputNum = src.width() * src.height();
        }
        
        if (paint) {
            // shader has no effect
            costPerPixel = paint->computePerPixelCost();
        } else {
            // default has blend
            costPerPixel = 1 + 1;
        }
        
        fDrawCost += inputNum + dst.width() * dst.height() * drawRatio * costPerPixel + doRealClipCost();
    }

    auto oh_options = OH_Drawing_SamplingOptionsCreate((OH_Drawing_FilterMode)sampling.filter,
                                                       (OH_Drawing_MipmapMode)sampling.mipmap);
    if (image->isOpaque() && imagePaint.getImageFilter() == nullptr && imagePaint.isSrcOver()) {
        imagePaint.setBlendMode(SkBlendMode::kSrc);
    }
    if (fEnableHash) {
        fHasher.push(&OH_Drawing_CanvasDrawPixelMapRect);
        updateHashWithState();
        if (image) {
            fHasher.push(as_IB(image)->hash());
        }
        fHasher.push(src);
        fHasher.push(dst);
        fHasher.push(sampling);
        fHasher.push(imagePaint.hash());
        fHasher.push(constraint);
    }

    attachPaint(imagePaint);
    OHDrawingAPI::OH_Drawing_CanvasDrawPixelMapRectConstraint(fDrawingCanvas, as_IB((SkImage *)image)->getOHPixelmap(),
                                                              (OH_Drawing_Rect *)&src, (OH_Drawing_Rect *)&dst,
                                                              oh_options, (OH_Drawing_SrcRectConstraint)constraint);
    
    detachPaint();
    OH_Drawing_SamplingOptionsDestroy(oh_options);
}

void SkCanvas::onDrawImageLattice2(const SkImage* image, const Lattice& lattice, const SkRect& dst,
                                   SkFilterMode filter, const SkPaint* paint) {
    RENDER_UNIMPLEMENTED;
    return;
//     SkPaint realPaint = clean_paint_for_drawImage(paint);
//
//     if (this->internalQuickReject(dst, realPaint)) {
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, realPaint, &dst);
//     if (layer) {
// //         this->topDevice()->drawImageLattice(image, lattice, dst, filter, layer->paint());
//     }
}

void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y,
                         const SkSamplingOptions& sampling, const SkPaint* paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    RETURN_ON_NULL(image);
    this->onDrawImage2(image, x, y, sampling, paint);
}

void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
                             const SkSamplingOptions& sampling, const SkPaint* paint,
                             SrcRectConstraint constraint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    // @ok
    RETURN_ON_NULL(image);
    if (!fillable(dst) || !fillable(src)) {
        return;
    }
    this->onDrawImageRect2(image, src, dst, sampling, paint, constraint);
}

void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst,
                             const SkSamplingOptions& sampling, const SkPaint* paint) {
    // @ok
    RETURN_ON_NULL(image);
    this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, sampling,
                        paint, kFast_SrcRectConstraint);
}

void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                              const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     auto glyphRunList = fScratchGlyphRunBuilder->blobToGlyphRunList(*blob, {x, y});
//     this->onDrawGlyphRunList(glyphRunList, paint);
}

void SkCanvas::onDrawGlyphRunList(const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
//     if (this->internalQuickReject(bounds, paint)) {
//         return;
//     }
//     auto layer = this->aboutToDraw(this, paint, &bounds);
//     if (layer) {
//         this->topDevice()->drawGlyphRunList(this, glyphRunList, paint, layer->paint());
//     }
}

sk_sp<Slug> SkCanvas::convertBlobToSlug(
        const SkTextBlob& blob, SkPoint origin, const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
    return nullptr;
//     TRACE_EVENT0("skia", TRACE_FUNC);
//     auto glyphRunList = fScratchGlyphRunBuilder->blobToGlyphRunList(blob, origin);
//     return this->onConvertGlyphRunListToSlug(glyphRunList, paint);
}

sk_sp<Slug>
SkCanvas::onConvertGlyphRunListToSlug(
        const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
//     if (bounds.isEmpty() || !bounds.isFinite() || paint.nothingToDraw()) {
//         return nullptr;
//     }
//     auto layer = this->aboutToDraw(this, paint, &bounds);
//     if (layer) {
//         return this->topDevice()->convertGlyphRunListToSlug(glyphRunList, paint, layer->paint());
//     }
    return nullptr;
}

void SkCanvas::drawSlug(const Slug* slug) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (slug) {
        this->onDrawSlug(slug);
    }
}

void SkCanvas::onDrawSlug(const Slug* slug) {
    RENDER_UNIMPLEMENTED;
//     SkRect bounds = slug->sourceBoundsWithOrigin();
//     if (this->internalQuickReject(bounds, slug->initialPaint())) {
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, slug->initialPaint(), &bounds);
//     if (layer) {
//         this->topDevice()->drawSlug(this, slug, layer->paint());
//     }
}



inline SkScalar getCharacterAdvance(const SkFont& font, const std::string& charStr, SkRect *bounds) {
    return font.measureText(charStr.c_str(), charStr.size(), SkTextEncoding::kUTF8, bounds);
}

// These call the (virtual) onDraw... method
void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding,
                              SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) {
    if (encoding == SkTextEncoding::kGlyphID) {
        SkDebugf("SkCanvas::drawSimpleText, Not support for SkTextEncoding::kGlyphID\n");
    }
    std::string utf8Str = convertToUTF8(text, byteLength, encoding);
    if (utf8Str.empty()) {
        SkDebugf("SkCanvas::drawSimpleText, utf8Str is empty\n");
        return;
    }

    SkRect textBounds = SkRect::MakeEmpty();
    font.measureText(utf8Str.data(), utf8Str.size(), SkTextEncoding::kUTF8, &textBounds);
    
    SkRect finalBounds = SkRect::MakeXYWH(
        x + textBounds.left(),
        y + textBounds.top(),
        textBounds.width(),
        textBounds.height()
    );
    float drawRatio = 0.f;
    bool intersect = markDrawBounds(finalBounds, &paint, false, &drawRatio);
    HANDLE_SUBCANVAS_DRAW_WITH_BOUNDS(drawSimpleText, text, byteLength, encoding, x, y, font, paint);
    if (fEnableHash) {
        updateHashWithState();
        fHasher.pushArray((const char *)text, byteLength);
        fHasher.push(encoding);
        fHasher.push(x);
        fHasher.push(y);
        fHasher.push(font.hash());
        fHasher.push(paint.hash());
    }
    
    if (OHRenderNode::gEnableRasterCache && !fForbidRasterCache && drawRatio > MIN_DRAW_RATIO) {
        fDrawCost +=
            (1 + paint.computePerPixelCost(finalBounds) * drawRatio) * finalBounds.width() * finalBounds.height() +
            doRealClipCost();
    }
    
    attachPaint(paint);
    const char *ptr = utf8Str.c_str();
    SkScalar drawX = x;
    const char *end = utf8Str.c_str() + utf8Str.size();
    while (ptr < end) {
        int charLen = 1;
        unsigned char lead = static_cast<unsigned char>(*ptr);
        if ((lead & 0x80) == 0) {
            charLen = 1;
        } else if ((lead & 0xE0) == 0xC0) {
            charLen = 2;
        } else if ((lead & 0xF0) == 0xE0) {
            charLen = 3;
        } else if ((lead & 0xF8) == 0xF0) {
            charLen = 4;
        } else {
            SkDebugf("SkCanvas::drawSimpleText, invalid UTF-8 character\n");
            break;
        }
        if (ptr + charLen > end) {
            SkDebugf("SkCanvas::drawSimpleText, truncated UTF-8 character\n");
            break;
        }

        std::string charStr(ptr, charLen);
        OH_Drawing_ErrorCode err = OH_Drawing_CanvasDrawSingleCharacter(fDrawingCanvas, charStr.c_str(), font, drawX, y);
        SkRect charBounds;
        drawX += getCharacterAdvance(font, charStr, &charBounds);
        ptr += charLen;
    }
    detachPaint();
//     TRACE_EVENT0("skia", TRACE_FUNC);
//     if (byteLength) {
//         sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength));
//         const sktext::GlyphRunList& glyphRunList =
//             fScratchGlyphRunBuilder->textToGlyphRunList(
//                     font, paint, text, byteLength, {x, y}, encoding);
//         if (!glyphRunList.empty()) {
//             this->onDrawGlyphRunList(glyphRunList, paint);
//         }
//     }
}

void SkCanvas::drawGlyphs(int count, const SkGlyphID* glyphs, const SkPoint* positions,
                          const uint32_t* clusters, int textByteCount, const char* utf8text,
                          SkPoint origin, const SkFont& font, const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     if (count <= 0) { return; }
//
//     sktext::GlyphRun glyphRun {
//             font,
//             SkSpan(positions, count),
//             SkSpan(glyphs, count),
//             SkSpan(utf8text, textByteCount),
//             SkSpan(clusters, count),
//             SkSpan<SkVector>()
//     };
//
//     sktext::GlyphRunList glyphRunList = fScratchGlyphRunBuilder->makeGlyphRunList(
//             glyphRun, paint, origin);
//     this->onDrawGlyphRunList(glyphRunList, paint);
}

void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[],
                          SkPoint origin, const SkFont& font, const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     if (count <= 0) { return; }
//
//     sktext::GlyphRun glyphRun {
//         font,
//         SkSpan(positions, count),
//         SkSpan(glyphs, count),
//         SkSpan<const char>(),
//         SkSpan<const uint32_t>(),
//         SkSpan<SkVector>()
//     };
//
//     sktext::GlyphRunList glyphRunList = fScratchGlyphRunBuilder->makeGlyphRunList(
//             glyphRun, paint, origin);
//     this->onDrawGlyphRunList(glyphRunList, paint);
}

void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[],
                          SkPoint origin, const SkFont& font, const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
//     if (count <= 0) { return; }
//
//     auto [positions, rotateScales] =
//             fScratchGlyphRunBuilder->convertRSXForm(SkSpan(xforms, count));
//
//     sktext::GlyphRun glyphRun {
//             font,
//             positions,
//             SkSpan(glyphs, count),
//             SkSpan<const char>(),
//             SkSpan<const uint32_t>(),
//             rotateScales
//     };
//     sktext::GlyphRunList glyphRunList = fScratchGlyphRunBuilder->makeGlyphRunList(
//             glyphRun, paint, origin);
//     this->onDrawGlyphRunList(glyphRunList, paint);
}

#if defined(SK_GANESH) && GR_TEST_UTILS
bool gSkBlobAsSlugTesting = false;
#endif

void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                            const SkPaint& paint) {
    attachPaint(paint);
    OH_Drawing_CanvasDrawTextBlob(fDrawingCanvas, *blob, x, y);
    detachPaint();
//     TRACE_EVENT0("skia", TRACE_FUNC);
//     RETURN_ON_NULL(blob);
//     RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite());
//
//     // Overflow if more than 2^21 glyphs stopping a buffer overflow latter in the stack.
//     // See chromium:1080481
//     // TODO: can consider unrolling a few at a time if this limit becomes a problem.
//     int totalGlyphCount = 0;
//     constexpr int kMaxGlyphCount = 1 << 21;
//     SkTextBlob::Iter i(*blob);
//     SkTextBlob::Iter::Run r;
//     while (i.next(&r)) {
//         int glyphsLeft = kMaxGlyphCount - totalGlyphCount;
//         RETURN_ON_FALSE(r.fGlyphCount <= glyphsLeft);
//         totalGlyphCount += r.fGlyphCount;
//     }
//
// #if defined(SK_GANESH) && GR_TEST_UTILS
//     // Draw using text blob normally or if the blob has RSX form because slugs can't convert that
//     // form.
//     if (!gSkBlobAsSlugTesting ||
//         this->topDevice()->asGaneshDevice() == nullptr ||
//         SkTextBlobPriv::HasRSXForm(*blob))
// #endif
//     {
//         this->onDrawTextBlob(blob, x, y, paint);
//     }
// #if defined(SK_GANESH) && GR_TEST_UTILS
//     else {
//         auto slug = Slug::ConvertBlob(this, *blob, {x, y}, paint);
//         slug->draw(this);
//     }
// #endif
}

void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
                                    const SkPaint& paint) {
    // @todo
    SkPaint simplePaint = clean_paint_for_drawVertices(paint);

    const SkRect& bounds = vertices->bounds();
    if (this->internalQuickReject(bounds, simplePaint)) {
        return;
    }

    auto layer = this->aboutToDraw(this, simplePaint, &bounds);
    if (layer) {
//         this->topDevice()->drawVertices(vertices, SkBlender::Mode(bmode), layer->paint());
    }
}

#ifdef SK_ENABLE_SKSL
void SkCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
    SkPaint simplePaint = clean_paint_for_drawVertices(paint);

    if (this->internalQuickReject(mesh.bounds(), simplePaint)) {
        return;
    }

    auto layer = this->aboutToDraw(this, simplePaint, nullptr);
    if (layer) {
        this->topDevice()->drawMesh(mesh, std::move(blender), paint);
    }
}
#endif

void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
                         const SkPoint texCoords[4], SkBlendMode bmode,
                         const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (nullptr == cubics) {
        return;
    }

    this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
}

void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                           const SkPoint texCoords[4], SkBlendMode bmode,
                           const SkPaint& paint) {
    RENDER_UNIMPLEMENTED;
    
//     // drawPatch has the same behavior restrictions as drawVertices
//     SkPaint simplePaint = clean_paint_for_drawVertices(paint);
//
//     // Since a patch is always within the convex hull of the control points, we discard it when its
//     // bounding rectangle is completely outside the current clip.
//     SkRect bounds;
//     bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts);
//     if (this->internalQuickReject(bounds, simplePaint)) {
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, simplePaint, &bounds);
//     if (layer) {
//         this->topDevice()->drawPatch(cubics, colors, texCoords, SkBlender::Mode(bmode),
//                                      layer->paint());
//     }
}

void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    TRACE_EVENT0("skia", TRACE_FUNC);
#endif
    RETURN_ON_NULL(dr);
    if (x || y) {
        SkMatrix matrix = SkMatrix::Translate(x, y);
        this->onDrawDrawable(dr, &matrix);
    } else {
        this->onDrawDrawable(dr, nullptr);
    }
}

void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
    TRACE_EVENT0("skia", TRACE_FUNC);
#endif
    RETURN_ON_NULL(dr);
    if (matrix && matrix->isIdentity()) {
        matrix = nullptr;
    }
    this->onDrawDrawable(dr, matrix);
}

void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
    RENDER_UNIMPLEMENTED;
//     // drawable bounds are no longer reliable (e.g. android displaylist)
//     // so don't use them for quick-reject
//     if (this->predrawNotify()) {
//         this->topDevice()->drawDrawable(this, dr, matrix);
//     }
}

void SkCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
                            const SkColor colors[], int count, SkBlendMode bmode,
                            const SkSamplingOptions& sampling, const SkRect* cull,
                            const SkPaint* paint) {
    RENDER_UNIMPLEMENTED;
    
//     // drawAtlas is a combination of drawVertices and drawImage...
//     SkPaint realPaint = clean_paint_for_drawVertices(clean_paint_for_drawImage(paint));
//     realPaint.setShader(atlas->makeShader(sampling));
//
//     if (cull && this->internalQuickReject(*cull, realPaint)) {
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, realPaint);
//     if (layer) {
//         this->topDevice()->drawAtlas(xform, tex, colors, count, SkBlender::Mode(bmode),
//                                      layer->paint());
//     }
}

void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
    RENDER_UNIMPLEMENTED;
    
//     SkASSERT(key);
//
//     if (this->predrawNotify()) {
//         this->topDevice()->drawAnnotation(rect, key, value);
//     }
}

void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA,
                                const SkColor4f& color, SkBlendMode mode) {
    RENDER_UNIMPLEMENTED;
    
//     SkASSERT(r.isSorted());
//
//     SkPaint paint{color};
//     paint.setBlendMode(mode);
//     if (this->internalQuickReject(r, paint)) {
//         return;
//     }
//
//     if (this->predrawNotify()) {
//         this->topDevice()->drawEdgeAAQuad(r, clip, edgeAA, color, mode);
//     }
}

void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count,
                                     const SkPoint dstClips[], const SkMatrix preViewMatrices[],
                                     const SkSamplingOptions& sampling, const SkPaint* paint,
                                     SrcRectConstraint constraint) {
    RENDER_UNIMPLEMENTED;
    
//     if (count <= 0) {
//         // Nothing to draw
//         return;
//     }
//
//     SkPaint realPaint = clean_paint_for_drawImage(paint);
//     SkSamplingOptions realSampling = clean_sampling_for_constraint(sampling, constraint);
//
//     // We could calculate the set's dstRect union to always check quickReject(), but we can't reject
//     // individual entries and Chromium's occlusion culling already makes it likely that at least one
//     // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1),
//     // or we need it for the autolooper (since it greatly improves image filter perf).
//     bool needsAutoLayer = SkToBool(realPaint.getImageFilter());
//     bool setBoundsValid = count == 1 || needsAutoLayer;
//     SkRect setBounds = imageSet[0].fDstRect;
//     if (imageSet[0].fMatrixIndex >= 0) {
//         // Account for the per-entry transform that is applied prior to the CTM when drawing
//         preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds);
//     }
//     if (needsAutoLayer) {
//         for (int i = 1; i < count; ++i) {
//             SkRect entryBounds = imageSet[i].fDstRect;
//             if (imageSet[i].fMatrixIndex >= 0) {
//                 preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds);
//             }
//             setBounds.joinPossiblyEmptyRect(entryBounds);
//         }
//     }
//
//     // If we happen to have the draw bounds, though, might as well check quickReject().
//     if (setBoundsValid && this->internalQuickReject(setBounds, realPaint)) {
//         return;
//     }
//
//     auto layer = this->aboutToDraw(this, realPaint, setBoundsValid ? &setBounds : nullptr);
//     if (layer) {
//         this->topDevice()->drawEdgeAAImageSet(imageSet, count, dstClips, preViewMatrices,
//                                               realSampling, layer->paint(), constraint);
//     }
}

//////////////////////////////////////////////////////////////////////////////
// These methods are NOT virtual, and therefore must call back into virtual
// methods, rather than actually drawing themselves.
//////////////////////////////////////////////////////////////////////////////

void SkCanvas::drawColor(const SkColor4f& c, SkBlendMode mode) {
    // @ok
    SkPaint paint;
    paint.setColor(c);
    paint.setBlendMode(mode);
    this->drawPaint(paint);
}

void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
    // @ok
    const SkPoint pt = { x, y };
    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
}

void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
    // @ok
    SkPoint pts[2];
    pts[0].set(x0, y0);
    pts[1].set(x1, y1);
    this->drawPoints(kLines_PointMode, 2, pts, paint);
}

void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) {
    if (radius < 0) {
        radius = 0;
    }

    SkRect  r;
    r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
    this->drawOval(r, paint);
}

void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
                             const SkPaint& paint) {
    if (rx > 0 && ry > 0) {
        SkRRect rrect;
        rrect.setRectXY(r, rx, ry);
        this->drawRRect(rrect, paint);
    } else {
        this->drawRect(r, paint);
    }
}

void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
                       SkScalar sweepAngle, bool useCenter,
                       const SkPaint& paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    if (oval.isEmpty() || !sweepAngle) {
        return;
    }
    this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
}

///////////////////////////////////////////////////////////////////////////////
#ifdef SK_DISABLE_SKPICTURE
void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {}


void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
                             const SkPaint* paint) {}
#else

void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
    // @ok
    TRACE_EVENT0("skia", TRACE_FUNC);
    RETURN_ON_NULL(picture);

    if (matrix && matrix->isIdentity()) {
        matrix = nullptr;
    }
    this->onDrawPicture(picture, matrix, paint);
}

void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
                             const SkPaint* paint) {
    // If we have fPendingDifferenceShape, we make the following drawPicture within this subCanvas
    if (fSubCanvas && !fIsPlayingBack && fSubCanvas->getSaveCount() == 1 &&
        std::holds_alternative<std::monostate>(fPendingDifferenceShape)) {
        finishSubCanvas();
    }
    if (fSubCanvas) {
        fSubCanvas->onDrawPicture(picture, matrix, paint);
        return;
    }
    int count = getSaveCount();
    if (paint) {
        SkRect newBounds = picture->cullRect();
        if (matrix) {
            matrix->mapRect(&newBounds);
        }
        saveLayer(&newBounds, paint);
    } else if (matrix) {
        save();
    }
    if (matrix) {
        concat(*matrix);
    }
    // drawCost will be added in playback.
    picture->playback(this);
    
    // No layer picture will be drawn in a new RenderNode.
    // markDrawBounds must be done after playback.
    if (picture->noLimitDraw()) {
        markDrawBounds(std::nullopt, nullptr, picture->isInNode());
    } else {
        markDrawBounds(picture->cullRect(), nullptr, picture->isInNode());
    }

    restoreToCount(count);

    if (!fSubCanvas && !fSubRecorder && !fNeedCreateSubCanvas && fEnableSubCanvas && fDrawingNode &&
        fDrawingNode->getPaintChildrenNum() > 0) {
        fNeedCreateSubCanvas = true;
    }
}

void SkCanvas::drawRenderNode(const std::shared_ptr<OHRenderNode> node, SkRect rect) {
    TRACE_EVENT0("skia", __FUNCTION__ );
    if (!fDrawingNode || !node) {
        return;
    }
    
    // If we have fPendingDifferenceShape, we make the following drawRenderNode within this subCanvas
    if (fSubCanvas && fSubCanvas->getSaveCount() == 1 &&
        std::holds_alternative<std::monostate>(fPendingDifferenceShape)) {
        finishSubCanvas();
    }
    if (fSubCanvas) {
        fSubCanvas->drawRenderNode(node, rect);
        return;
    }
    
    bool intersect = markDrawBounds(rect, nullptr, true);
    
    node->setParent(fDrawingNode);

    SkRect local_paint_area = rect;
    
    if (hasClip()) {
        auto local_cull_shape = getLocalClipShape();
        if (!std::holds_alternative<std::monostate>(local_cull_shape)) {
            node->updateClipArea(local_cull_shape);
            
            auto local_clip_rect = GetClipShapeBounds(local_cull_shape);
            if (!local_paint_area.intersect(local_clip_rect)) {
                local_paint_area = SkRect::MakeEmpty();
            }
        }
    } else {
        node->unsetClipArea();
        if (hasPendingDifferenceShape()) {
            node->updatePendingDiffShape(getPendingDifferenceShape());
        }
    }
    
    node->setFixedFrame(rect);
    
    SkMatrix father_matrix = getTotalMatrix();
    node->updateFatherMatrix(father_matrix);
    
    SkRect device_paint_area;
    father_matrix.mapRect(&device_paint_area, local_paint_area);
    
    node->setPaintArea(device_paint_area);
    
    fDrawingNode->appendChild(node);
    fDrawingNode->markForbidRasterCache();
    markForbidRasterCache();
    
    if (!fSubCanvas && !fSubRecorder && !fNeedCreateSubCanvas && fEnableSubCanvas && fDrawingNode) {
        fNeedCreateSubCanvas = true;
    }
    
}


#endif

///////////////////////////////////////////////////////////////////////////////

SkCanvas::ImageSetEntry::ImageSetEntry() = default;
SkCanvas::ImageSetEntry::~ImageSetEntry() = default;
SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default;
SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default;

SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
                                       const SkRect& dstRect, int matrixIndex, float alpha,
                                       unsigned aaFlags, bool hasClip)
                : fImage(std::move(image))
                , fSrcRect(srcRect)
                , fDstRect(dstRect)
                , fMatrixIndex(matrixIndex)
                , fAlpha(alpha)
                , fAAFlags(aaFlags)
                , fHasClip(hasClip) {}

SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect,
                                       const SkRect& dstRect, float alpha, unsigned aaFlags)
                : fImage(std::move(image))
                , fSrcRect(srcRect)
                , fDstRect(dstRect)
                , fAlpha(alpha)
                , fAAFlags(aaFlags) {}

///////////////////////////////////////////////////////////////////////////////

std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
                                                     size_t rowBytes, const SkSurfaceProps* props) {
    RENDER_UNIMPLEMENTED;
    return nullptr;
//     if (!SkSurfaceValidateRasterInfo(info, rowBytes)) {
//         return nullptr;
//     }
//
//     SkBitmap bitmap;
//     if (!bitmap.installPixels(info, pixels, rowBytes)) {
//         return nullptr;
//     }
//
//     return props ?
//         std::make_unique<SkCanvas>(bitmap, *props) :
//         std::make_unique<SkCanvas>(bitmap);
}

void SkCanvas::attachPaint(const SkPaint& paint) {
    SkPaint::Style paint_style = paint.getStyle();
    paint.updateOHPainter();
    if (paint_style == SkPaint::Style::kFill_Style || paint_style == SkPaint::Style::kStrokeAndFill_Style) {
        OH_Drawing_CanvasAttachBrush(fDrawingCanvas, paint);
    }
    if (paint_style == SkPaint::Style::kStroke_Style || paint_style == SkPaint::Style::kStrokeAndFill_Style) {
        OH_Drawing_CanvasAttachPen(fDrawingCanvas, paint);
    }
}

void SkCanvas::detachPaint() {
    OH_Drawing_CanvasDetachBrush(fDrawingCanvas);
    OH_Drawing_CanvasDetachPen(fDrawingCanvas);
}

///////////////////////////////////////////////////////////////////////////////

// SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
//     : INHERITED(SkIRect::MakeWH(width, height)) {}
//
// SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
//     : INHERITED(bounds) {}
//
// SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device)
//     : INHERITED(device) {}
//
// SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
//     (void)this->INHERITED::getSaveLayerStrategy(rec);
//     return kNoLayer_SaveLayerStrategy;
// }
//
// bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) {
//     return false;
// }

///////////////////////////////////////////////////////////////////////////////

static_assert((int)SkRegion::kDifference_Op == (int)SkClipOp::kDifference, "");
static_assert((int)SkRegion::kIntersect_Op  == (int)SkClipOp::kIntersect, "");

///////////////////////////////////////////////////////////////////////////////////////////////////

SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
    RENDER_UNIMPLEMENTED;
//     const SkBaseDevice* dev = this->topDevice();
//     if (fAllocator) {
//         SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
//         SkIRect clip = dev->devClipBounds();
//         if (!clip.intersect({0, 0, dev->width(), dev->height()})) {
//             clip.setEmpty();
//         }
//
//         fAllocator->updateHandle(handle, dev->localToDevice(), clip);
//         return handle;
//     }
    return nullptr;
}

static bool install(SkBitmap* bm, const SkImageInfo& info,
                    const SkRasterHandleAllocator::Rec& rec) {
    RENDER_UNIMPLEMENTED;
    return false;
//     return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx);
}

SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
                                                                     SkBitmap* bm) {
    RENDER_UNIMPLEMENTED;
    return nullptr;
//     SkRasterHandleAllocator::Rec rec;
//     if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
//         return nullptr;
//     }
//     return rec.fHandle;
}

std::unique_ptr<SkCanvas>
SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
                                    const SkImageInfo& info, const Rec* rec,
                                    const SkSurfaceProps* props) {
    RENDER_UNIMPLEMENTED;
    return nullptr;
//     if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) {
//         return nullptr;
//     }
//
//     SkBitmap bm;
//     Handle hndl;
//
//     if (rec) {
//         hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
//     } else {
//         hndl = alloc->allocBitmap(info, &bm);
//     }
//     return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl, props))
//                 : nullptr;
}


bool SkCanvas::markDrawBounds(std::optional<SkRect> area, const SkPaint *paint, bool isNodeMode, float *drawAreaRatio) {
    std::optional<SkRect> deviceArea;
    bool intersect = true;
    if (area.has_value()) {
        deviceArea = adjustAndMap(area.value(), isNodeMode ? nullptr : paint);
        // > 0.1f => avoid /0.f = inf
        if (drawAreaRatio != nullptr && (area->width() * area->height()) > 0.1f) {
            *drawAreaRatio = deviceArea->width() * deviceArea->height() / (area->width() * area->height());
        }
    } else {
        deviceArea = fCullRect;
    }

    std::optional<SkRect> node_area;
    if (fDrawingNode) {
        node_area = fDrawingNode->getPaintChildArea();
    }

    if (deviceArea.has_value() && fDrawBounds.has_value()) {
        if (node_area.has_value()) {
            intersect = (node_area.value().intersect(deviceArea.value()) && node_area.value().width() > 0.1f &&
                         node_area.value().height() > 0.1f);
        }
        fDrawBounds->join(deviceArea.value());
    } else {
        fDrawBounds = std::nullopt;
        if (node_area.has_value() && node_area.value().isEmpty()) {
            intersect = false;
        }
    }
    return intersect;
}

bool SkCanvas::markDrawBounds(SkColor color) {
    bool intersect = true;
    if (fCullRect.has_value() && fDrawBounds.has_value()) {
        intersect = fDrawBounds->intersects(fCullRect.value());
        fDrawBounds->join(fCullRect.value());
    } else {
        fDrawBounds = std::nullopt;
    }
    return intersect;
}

void SkCanvas::markCullShape(const SkClipShape& cull, SkClipOp op) {
    if (std::holds_alternative<SkRect>(fClipShape) && std::get<SkRect>(fClipShape).isEmpty()) {
        return;
    }
    SkClipShape mappedCull = SkRect::MakeEmpty();
    SkRect rectCull = SkRect::MakeEmpty();
    if (!fNotInv) {
        if (std::holds_alternative<SkRect>(cull)) {
            SkRect rect = std::get<SkRect>(cull);
            fCTM.mapRect(&rect);
            mappedCull = rect;
            rectCull = rect;
        } else if (std::holds_alternative<SkRRect>(cull)) {
            const SkRRect& rrect = std::get<SkRRect>(cull);
            SkRRect real_rrect;
            rrect.transform(fCTM, &real_rrect);
            mappedCull = real_rrect;
            rectCull = real_rrect.rect();
        } else if (std::holds_alternative<SkPath>(cull)) {
            SkPath path = std::get<SkPath>(cull);
            path.transform(fCTM);
            mappedCull = path;
            rectCull = path.getBounds();
        }
    }
    if (op == SkClipOp::kIntersect) {
        if (std::holds_alternative<std::monostate>(fClipShape)) {
            fClipShape = mappedCull;
            // processing for difference op
            if (!std::holds_alternative<std::monostate>(fPendingDifferenceShape)) {
                fClipShape = differenceShape(fClipShape, fPendingDifferenceShape);
                fPendingDifferenceShape = std::monostate();
            }
        } else {
            fClipShape = intersectShape(fClipShape, mappedCull);
        }
        if (!fCullRect.has_value()) {
            fCullRect = rectCull;
        } else {
            if (!fCullRect->intersect(rectCull)) {
                fCullRect = SkRect::MakeEmpty();
            }
        }
    } else if (op == SkClipOp::kDifference) {
        if (std::holds_alternative<std::monostate>(fClipShape)) {
            if (!std::holds_alternative<std::monostate>(fPendingDifferenceShape)) {
                fPendingDifferenceShape = unionShape(fPendingDifferenceShape, mappedCull);
            } else {
                fPendingDifferenceShape = mappedCull;
            }
        } else {
            fClipShape = differenceShape(fClipShape, mappedCull);
        }
    }
    if (OHRenderNode::gEnableRasterCache) {
        fNeedDoClip = true;
    }
}

bool contains(const SkRRect &a, const SkRRect &b) {
    if (a.getBounds().contains(b.getBounds())) {
        bool ret = true;
        for (int i = 0; i < 4; ++i) {
            SkVector r1 = a.radii(SkRRect::Corner(i));
            SkVector r2 = b.radii(SkRRect::Corner(i));
            if (r1.fX < r2.fX || r1.fY < r2.fY) {
                ret = false;
                break;
            }
        }
        if (ret) {
            return true;
        }
    }
    return false;
}

inline SkClipShape ClipShapeOp(const SkClipShape &a, const SkClipShape &b, SkPathOp op) {
    SkASSERT((!std::holds_alternative<std::monostate>(a)) && (!std::holds_alternative<std::monostate>(b)));
    SkASSERT(op == SkPathOp::kIntersect_SkPathOp || op == SkPathOp::kDifference_SkPathOp ||
             op == SkPathOp::kUnion_SkPathOp);

    // @NOTE: The "setCommandPath" interface cannot set the fillType of path.
    // When the path contains holes, the holes may not be rendered correctly—only when
    // the inner and outer paths have opposite winding directions will the holes display as expected.

    // @TODO, get the outer SkPathDirection of SkPath, now we assume all SkPathDirection is kCW

    auto shapeToPath = [](const SkClipShape& s, SkPathDirection dir = SkPathDirection::kCW) -> SkPath {
        SkPath p;
        if (std::holds_alternative<SkRect>(s)) {
            p.addRect(std::get<SkRect>(s), dir);
        } else if (std::holds_alternative<SkRRect>(s)) {
            p.addRRect(std::get<SkRRect>(s), dir);
        } else if (std::holds_alternative<SkPath>(s)) {
            if (dir == SkPathDirection::kCCW) {
                p.reverseAddPath(std::get<SkPath>(s));
            } else [[likely]] {
                p = std::get<SkPath>(s);
            }
        }
        return p;
    };

//    constexpr auto OppositeDirection = [](SkPathDirection dir) constexpr -> SkPathDirection {
//        return (dir == SkPathDirection::kCW) ? SkPathDirection::kCCW : SkPathDirection::kCW;
//    };
//
    auto dir = ((op == kDifference_SkPathOp) ? SkPathDirection::kCCW : SkPathDirection::kCW);
    SkPath pa = shapeToPath(a);
    SkPath pb = shapeToPath(b, dir);
    SkPath out;

    if (!Op(pa, pb, op, &out)) {
        return SkRect::MakeEmpty();
    }

    SkRect outRect;
    if (out.isRect(&outRect)) {
        return outRect;
    }
    SkRRect outRRect;
    if (out.isRRect(&outRRect)) {
        return outRRect;
    }
    if (!out.isEmpty()) {
        return out;
    }

    return SkRect::MakeEmpty();
}

SkClipShape SkCanvas::intersectShape(const SkClipShape& a, const SkClipShape& b) {
    SkASSERT((!std::holds_alternative<std::monostate>(a)) && (!std::holds_alternative<std::monostate>(b)));
    // rect & rect
    if (std::holds_alternative<SkRect>(a) && std::holds_alternative<SkRect>(b)) {
        const SkRect &r1 = std::get<SkRect>(a);
        const SkRect &r2 = std::get<SkRect>(b);
        SkRect out;
        if (out.intersect(r1, r2)) {
            return out;
        } else {
            return SkRect::MakeEmpty();
        }
    }
    // rrect & rrect contains
    if (std::holds_alternative<SkRRect>(a) && std::holds_alternative<SkRRect>(b)) {
        const SkRRect &rr1 = std::get<SkRRect>(a);
        const SkRRect &rr2 = std::get<SkRRect>(b);
        if (contains(rr1, rr2)) {
            return rr2;
        }
        if (contains(rr2, rr1)) {
            return rr1;
        }
    }
    // normal rrect & rrect
    if (!(std::holds_alternative<SkPath>(a) || std::holds_alternative<SkPath>(b))) {
        SkRRect rr1;
        SkRRect rr2;
        if (std::holds_alternative<SkRect>(a)) {
            rr1.setRect(std::get<SkRect>(a));
        } else {
            rr1 = std::get<SkRRect>(a);
        }
        if (std::holds_alternative<SkRect>(b)) {
            rr2.setRect(std::get<SkRect>(b));
        } else {
            rr2 = std::get<SkRRect>(b);
        }
        SkRRect out = SkRRectPriv::ConservativeIntersect(rr1, rr2);
        if (!out.isEmpty()) {
            return out;
        }
    }
    return ClipShapeOp(a, b, SkPathOp::kIntersect_SkPathOp);
}

SkClipShape SkCanvas::differenceShape(const SkClipShape& a, const SkClipShape& b) {
    SkASSERT((!std::holds_alternative<std::monostate>(a)) && (!std::holds_alternative<std::monostate>(b)));
    SkRect boundsA = GetClipShapeBounds(a);
    SkRect boundsB = GetClipShapeBounds(b);
    if (boundsB.isEmpty() || !boundsA.intersects(boundsB)) {
        return a;
    }
    if (std::holds_alternative<SkRect>(a) && std::holds_alternative<SkRect>(b)) {
        const SkRect& ra = std::get<SkRect>(a);
        const SkRect& rb = std::get<SkRect>(b);
        if (rb.contains(ra)) {
            return SkRect::MakeEmpty();
        }
    }
    if (std::holds_alternative<SkRRect>(a) && std::holds_alternative<SkRRect>(b)) {
        const SkRRect& rra = std::get<SkRRect>(a);
        const SkRRect& rrb = std::get<SkRRect>(b);
        if (contains(rrb, rra)) {
            return SkRect::MakeEmpty();
        }
    }
    return ClipShapeOp(a, b, SkPathOp::kDifference_SkPathOp);
}

SkClipShape SkCanvas::unionShape(const SkClipShape& a, const SkClipShape& b) {
    SkASSERT((!std::holds_alternative<std::monostate>(a)) && (!std::holds_alternative<std::monostate>(b)));
    SkRect boundsA = GetClipShapeBounds(a);
    SkRect boundsB = GetClipShapeBounds(b);

    if (!boundsA.intersect(boundsB)) {
        return ClipShapeOp(a, b, SkPathOp::kUnion_SkPathOp);
    }
    auto shape_contains_bounds = [](const SkClipShape& shape, const SkRect& bounds) -> bool {
        if (std::holds_alternative<SkRect>(shape)) {
            return std::get<SkRect>(shape).contains(bounds);
        }
        if (std::holds_alternative<SkRRect>(shape)) {
            SkRect innerRect = SkRRectPriv::InnerBounds(std::get<SkRRect>(shape));
            return innerRect.contains(bounds);
        }
        return false;
    };
    if (shape_contains_bounds(a, boundsB)) {
        return a;
    }
    if (shape_contains_bounds(b, boundsA)) {
        return b;
    }
    return ClipShapeOp(a, b, SkPathOp::kUnion_SkPathOp);
//    if (std::holds_alternative<SkRect>(a)) {
//        if (std::get<SkRect>(a).contains(boundsB)) {
//            return a;
//        }
//    }
//
//    if (std::holds_alternative<SkRect>(b)) {
//        if (std::get<SkRect>(b).contains(boundsA)) {
//            return b;
//        }
//    }
//
//    if (std::holds_alternative<SkRRect>(a)) {
//        SkRect innerRect = SkRRectPriv::InnerBounds(std::get<SkRRect>(a));
//        if (innerRect.contains(boundsB)) {
//            return a;
//        }
//    }
//
//    if (std::holds_alternative<SkRRect>(b)) {
//        SkRect innerRect = SkRRectPriv::InnerBounds(std::get<SkRRect>(b));
//        if (innerRect.contains(boundsA)) {
//            return b;
//        }
//    }
}

std::optional<SkRect> SkCanvas::getFinishDrawBounds() {
    restoreToCount(fInitSaveCount);
    return fDrawBounds;
}

static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
    if (paint) {
        if (paint->canComputeFastBounds()) {
            *rect = paint->computeFastBounds(*rect, rect);
            return true;
        }
        return false;
    }
    return true;
}

bool SkCanvas::adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore) const {
    
    for (int i = fStateStack.size() - 1 - savesToIgnore; i >= 0; i--) {
        if (!fStateStack[i].fIsLayer) {
            break;
        }
        if (!fStateStack[i].fPaint.has_value()) {
            continue;
        }
        if (fStateStack[i].fNotInv) {
            return false;
        }
        fStateStack[i].fInvTransform.mapRect(rect);
        if (fStateStack[i].fPaint.has_value() && !AdjustForPaint(&fStateStack[i].fPaint.value(), rect)) {
            return false;
        }
        fStateStack[i].fTransform.mapRect(rect);
    }
    return true;
}

// Adjust rect for all paints that may affect its geometry, then map it to identity space.
std::optional<SkRect> SkCanvas::adjustAndMap(SkRect rect, const SkPaint* paint) const {
    // Inverted rectangles really confuse our BBHs.
    rect.sort();

    // Adjust the rect for its own paint.
    if (!AdjustForPaint(paint, &rect)) {
        // The paint could do anything to our bounds.  The only safe answer is the cull.
        return fCullRect;
    }

    // this is a skia's bug when calculate the real paint area.
    // Map the rect back to identity space.
    fCTM.mapRect(&rect);

    // Adjust rect for all the paints from the SaveLayers we're inside.
    if (!this->adjustForSaveLayerPaints(&rect)) {
        // Same deal as above.
        return fCullRect;
    }

    // Nothing can draw outside the cull rect.
    if (fCullRect.has_value() && !rect.intersect(fCullRect.value())) {
        return SkRect::MakeEmpty();
    }

    return rect;
}

void SkCanvas::beginSubCanvas() {
    SkASSERT(fNeedCreateSubCanvas && !fSubCanvas && !fSubRecorder && fEnableSubCanvas);
    if (getRenderNode()) {
        fSubRecorder = getRenderNode()->getOrCreateNextSubRecorder();
        SkRect bounds = this->getLocalClipBounds();
        if (bounds.isEmpty()) {
            bounds = SkRect::MakeWH(4096, 4096);
        }
        fSubCanvas = fSubRecorder->beginRecording(bounds);
        fNeedCreateSubCanvas = false;
    }
}

// finishSubCanvas should be invoke by finishRecordingAsPicture or restoreToCount
// which fPendingDifferenceShape can be reset safely
void SkCanvas::finishSubCanvas() {
    SkASSERT(!fNeedCreateSubCanvas && fSubCanvas && fSubRecorder && fSubCanvas->getSaveCount() == 1);
    
    auto subMat = fSubCanvas->getTotalMatrix();
    auto subInvMat = fSubCanvas->getInvMatrix();
    auto subNotInv = fSubCanvas->getNotInv();
    auto subClip = fSubCanvas->getLocalClipShape();
    // auto pendingClipShape = fSubCanvas->getPendingDifferenceShape();

    sk_sp<SkPicture> subPic = fSubRecorder->finishRecordingAsPicture();
    fSubPics.push_back(subPic);
    
    fSubCanvas = nullptr;
    // fSubRecorder will be recycled: restartChildAdd move fSubRecorderNextIt to begin()
    fSubRecorder = nullptr;

    fIsPlayingBack = true;
    markSubPicPlayback(true);
    this->drawPicture(subPic);
    markSubPicPlayback(false);
    fIsPlayingBack = false;

//    OH_Drawing_CanvasDrawRecordCmd(*this, *subPic);

//    delete fSubRecorder;

    // 将子canvas的变换 apply 到主 canvas
    if (fPendingSubCanvasShouldReset) {
        if (fNotInv) {
            OH_Drawing_CanvasSetMatrix(fDrawingCanvas, subMat);
        } else {
            OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, fInvCTM);
            OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, subMat);
        }
        fCTM = subMat;
        fInvCTM = subInvMat;
        fNotInv = subNotInv;
    } else {
        fCTM.preConcat(subMat);
        OH_Drawing_CanvasConcatMatrix(fDrawingCanvas, subMat);
        if (subNotInv) {
            fNotInv = true;
        } else {
            fInvCTM.postConcat(subInvMat);
        }
    }
    fPendingSubCanvasShouldReset = false;

    auto applyClip = [&](const auto& clip, SkClipOp op) {
        if (std::holds_alternative<SkRect>(clip)) {
            clipRect(std::get<SkRect>(clip), op);
        } else if (std::holds_alternative<SkRRect>(clip)) {
            clipRRect(std::get<SkRRect>(clip), op);
        } else if (std::holds_alternative<SkPath>(clip)) {
            clipPath(std::get<SkPath>(clip), op);
        }
    };

    if (!std::holds_alternative<std::monostate>(subClip)) {
        applyClip(subClip, SkClipOp::kIntersect);
    }
    // if (!std::holds_alternative<std::monostate>(pendingClipShape)) {
    //     applyClip(pendingClipShape, SkClipOp::kDifference);
    // }

}

//int SkCanvas::getPixelCostForPaint(const SkPaint& paint) const {
//    int ret = COST_PER_PIXEL_BASE;
//    if (paint.getShader()) {
//        ret += COST_PER_PIXEL_SHADER;
//    }
//    if (paint.asBlendMode().has_value() && paint.asBlendMode().value() != SkBlendMode::kSrcOver) {
//        ret += COST_PER_PIXEL_BLENDMODE;
//    }
//    if (paint.getColorFilter()) {
//        ret += COST_PER_PIXEL_COLORFILTER;
//    }
//    if (paint.getAlphaf() < 0.99f) {
//        ret += COST_PER_PIXEL_ALPHA;
//    }
//    if (paint.getMaskFilter()) {
//        ret += COST_PER_PIXEL_MASKFILTER;
//    }
//    if (paint.getImageFilter()) {
//        ret += COST_PER_PIXEL_IMAGEFILTER;
//    }
//    if (paint.getPathEffect()) {
//        ret += COST_PER_PIXEL_PATHEFFECT;
//    }
//    if (paint.isAntiAlias()) {
//        ret += COST_PER_PIXEL_ANTIALIAS;
//    }
//    return ret;
//}

float SkCanvas::getAreaForPaint(const SkRect &bounds, const SkPaint &paint, SkRectType type) const {
    float area = 0.0f;

    if (type == SkRectType::kRect) {
        switch (paint.getStyle()) {
            case SkPaint::kFill_Style:
                area = bounds.width() * bounds.height();
                break;
            case SkPaint::kStroke_Style: {
                float stroke = std::max(1.0f, paint.getStrokeWidth());
                area = 2 * (bounds.width() + bounds.height()) * stroke;
                break;
            }
            case SkPaint::kStrokeAndFill_Style: {
                float fillArea = bounds.width() * bounds.height();
                float stroke = std::max(1.0f, paint.getStrokeWidth());
                float strokeArea = 2 * (bounds.width() + bounds.height()) * stroke;
                area = fillArea + strokeArea;
                break;
            }
            default:
                area = bounds.width() * bounds.height();
                break;
        }
    } else if (type == SkRectType::kOval) {
        float a = bounds.width() / 2.0f;
        float b = bounds.height() / 2.0f;

        switch (paint.getStyle()) {
            case SkPaint::kFill_Style:
                area = static_cast<float>(M_PI) * a * b;
                break;
            case SkPaint::kStroke_Style: {
                float stroke = std::max(1.0f, paint.getStrokeWidth());
                // Ramanujan's approximation for ellipse perimeter
                float h = (a - b) * (a - b) / ((a + b) * (a + b));
                float perimeter = static_cast<float>(M_PI) * (a + b) * (1.0f + 3.0f * h / (10.0f + std::sqrt(4.0f - 3.0f * h)));
                area = perimeter * stroke;
                break;
            }
            case SkPaint::kStrokeAndFill_Style: {
                float fillArea = static_cast<float>(M_PI) * a * b;
                float stroke = std::max(1.0f, paint.getStrokeWidth());
                float h = (a - b) * (a - b) / ((a + b) * (a + b));
                float perimeter = static_cast<float>(M_PI) * (a + b) * (1.0f + 3.0f * h / (10.0f + std::sqrt(4.0f - 3.0f * h)));
                float strokeArea = perimeter * stroke;
                area = fillArea + strokeArea;
                break;
            }
            default:
                area = static_cast<float>(M_PI) * a * b;
                break;
        }
    } else {
        area = bounds.width() * bounds.height();
    }

    return std::max(1.0f, area);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(SK_GANESH) && GR_TEST_UTILS
SkTestCanvas<SkSlugTestKey>::SkTestCanvas(SkCanvas* canvas)
        : SkCanvas(sk_ref_sp(canvas->baseDevice())) {}

void SkTestCanvas<SkSlugTestKey>::onDrawGlyphRunList(
        const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
    SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
    if (this->internalQuickReject(bounds, paint)) {
        return;
    }
    auto layer = this->aboutToDraw(this, paint, &bounds);
    if (layer) {
        if (glyphRunList.hasRSXForm()) {
            this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint());
        } else {
            auto slug = this->onConvertGlyphRunListToSlug(glyphRunList, layer->paint());
            this->drawSlug(slug.get());
        }
    }
}

SkTestCanvas<SkSerializeSlugTestKey>::SkTestCanvas(SkCanvas* canvas)
        : SkCanvas(sk_ref_sp(canvas->baseDevice())) {}

void SkTestCanvas<SkSerializeSlugTestKey>::onDrawGlyphRunList(
        const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
    SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
    if (this->internalQuickReject(bounds, paint)) {
        return;
    }
    auto layer = this->aboutToDraw(this, paint, &bounds);
    if (layer) {
        if (glyphRunList.hasRSXForm()) {
            this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint());
        } else {
            sk_sp<SkData> bytes;
            {
                auto slug = this->onConvertGlyphRunListToSlug(glyphRunList, layer->paint());
                if (slug != nullptr) {
                    bytes = slug->serialize();
                }
            }
            {
                if (bytes != nullptr) {
                    auto slug = Slug::Deserialize(bytes->data(), bytes->size());
                    this->drawSlug(slug.get());
                }
            }
        }
    }
}

// A do nothing handle manager for the remote strike server.
class ServerHandleManager : public SkStrikeServer::DiscardableHandleManager {
public:
    SkDiscardableHandleId createHandle() override {
        return 0;
    }

    bool lockHandle(SkDiscardableHandleId id) override {
        return true;
    }

    bool isHandleDeleted(SkDiscardableHandleId id) override {
        return false;
    }
};

// Lock the strikes into the cache for the length of the test. This handler is tied to the lifetime
// of the canvas used to render the entire test.
class ClientHandleManager : public SkStrikeClient::DiscardableHandleManager {
public:
    bool deleteHandle(SkDiscardableHandleId id) override {
        return fIsLocked;
    }

    void assertHandleValid(SkDiscardableHandleId id) override {
        DiscardableHandleManager::assertHandleValid(id);
    }

    void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override {

    }

    void notifyReadFailure(const ReadFailureData& data) override {
        DiscardableHandleManager::notifyReadFailure(data);
    }

    void unlock() {
        fIsLocked = true;
    }

private:
    bool fIsLocked{false};
};

SkTestCanvas<SkRemoteSlugTestKey>::SkTestCanvas(SkCanvas* canvas)
        : SkCanvas(sk_ref_sp(canvas->baseDevice()))
        , fServerHandleManager(new ServerHandleManager{})
        , fClientHandleManager(new ClientHandleManager{})
        , fStrikeServer(fServerHandleManager.get())
        , fStrikeClient(fClientHandleManager) {}

// Allow the strikes to be freed from the strike cache after the test has been drawn.
SkTestCanvas<SkRemoteSlugTestKey>::~SkTestCanvas() {
    static_cast<ClientHandleManager*>(fClientHandleManager.get())->unlock();
}

void SkTestCanvas<SkRemoteSlugTestKey>::onDrawGlyphRunList(
        const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
    SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
    if (this->internalQuickReject(bounds, paint)) {
        return;
    }
    auto layer = this->aboutToDraw(this, paint, &bounds);
    if (layer) {
        if (glyphRunList.hasRSXForm()) {
            this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint());
        } else {
            sk_sp<SkData> slugBytes;
            std::vector<uint8_t> glyphBytes;
            {
                auto analysisCanvas = fStrikeServer.makeAnalysisCanvas(
                        this->topDevice()->width(),
                        this->topDevice()->height(),
                        this->fProps,
                        this->topDevice()->imageInfo().refColorSpace(),
                        // TODO: Where should we get this value from?
                        /*DFTSupport=*/ true);

                // TODO: Move the analysis canvas processing up to the via to handle a whole
                //  document at a time. This is not the correct way to handle the CTM; it doesn't
                //  work for layers.
                analysisCanvas->setMatrix(this->getLocalToDevice());
                auto slug = analysisCanvas->onConvertGlyphRunListToSlug(glyphRunList,
                                                                        layer->paint());
                if (slug != nullptr) {
                    slugBytes = slug->serialize();
                }
                fStrikeServer.writeStrikeData(&glyphBytes);
            }
            {
                if (!glyphBytes.empty()) {
                    fStrikeClient.readStrikeData(glyphBytes.data(), glyphBytes.size());
                }
                if (slugBytes != nullptr) {
                    auto slug = Slug::Deserialize(
                            slugBytes->data(), slugBytes->size(), &fStrikeClient);
                    this->drawSlug(slug.get());
                }
            }
        }
    }
}
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
